From c6e5c4cca485d55b2317850cc1f178a29e329f23 Mon Sep 17 00:00:00 2001 From: Ricard Vilalta Date: Mon, 14 Feb 2022 08:47:45 -0500 Subject: [PATCH 001/353] Working on first implementation of Interdomain service --- src/interdomain/.gitlab-ci.yml | 88 + src/interdomain/Config.py | 18 + src/interdomain/Dockerfile | 39 + src/interdomain/__init__.py | 0 src/interdomain/client/InterdomainClient.py | 55 + src/interdomain/client/__init__.py | 0 src/interdomain/genproto.sh | 37 + src/interdomain/proto/__init__.py | 0 src/interdomain/proto/context_pb2.py | 2662 +++++++++++++++++ src/interdomain/proto/interdomain_pb2.py | 89 + src/interdomain/proto/service_pb2.py | 88 + src/interdomain/proto/service_pb2_grpc.py | 165 + src/interdomain/requirements.in | 18 + src/interdomain/service/InterdomainService.py | 60 + .../service/InterdomainServiceServicerImpl.py | 163 + src/interdomain/service/__init__.py | 0 src/interdomain/service/__main__.py | 62 + 17 files changed, 3544 insertions(+) create mode 100644 src/interdomain/.gitlab-ci.yml create mode 100644 src/interdomain/Config.py create mode 100644 src/interdomain/Dockerfile create mode 100644 src/interdomain/__init__.py create mode 100644 src/interdomain/client/InterdomainClient.py create mode 100644 src/interdomain/client/__init__.py create mode 100755 src/interdomain/genproto.sh create mode 100644 src/interdomain/proto/__init__.py create mode 100644 src/interdomain/proto/context_pb2.py create mode 100644 src/interdomain/proto/interdomain_pb2.py create mode 100644 src/interdomain/proto/service_pb2.py create mode 100644 src/interdomain/proto/service_pb2_grpc.py create mode 100644 src/interdomain/requirements.in create mode 100644 src/interdomain/service/InterdomainService.py create mode 100644 src/interdomain/service/InterdomainServiceServicerImpl.py create mode 100644 src/interdomain/service/__init__.py create mode 100644 src/interdomain/service/__main__.py diff --git a/src/interdomain/.gitlab-ci.yml b/src/interdomain/.gitlab-ci.yml new file mode 100644 index 000000000..e4da10db7 --- /dev/null +++ b/src/interdomain/.gitlab-ci.yml @@ -0,0 +1,88 @@ +# Build, tag and push the Docker image to the GitLab registry +build service: + variables: + IMAGE_NAME: 'service' # name of the microservice + IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) + stage: build + before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + script: + - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/Dockerfile ./src/ + - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" + - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" + after_script: + - docker images --filter="dangling=true" --quiet | xargs -r docker rmi + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)' + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"' + - changes: + - src/$IMAGE_NAME/**/*.{py,in,yml} + - src/$IMAGE_NAME/Dockerfile + - src/$IMAGE_NAME/tests/*.py + - manifests/${IMAGE_NAME}service.yaml + - .gitlab-ci.yml + +# Apply unit test to the component +unit test service: + variables: + IMAGE_NAME: 'service' # name of the microservice + IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) + stage: unit_test + needs: + - build service + before_script: + - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + - if docker network list | grep teraflowbridge; then echo "teraflowbridge is already created"; else docker network create -d bridge teraflowbridge; fi + - if docker container ls | grep $IMAGE_NAME; then docker rm -f $IMAGE_NAME; else echo "$IMAGE_NAME image is not in the system"; fi + script: + - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG" + - docker run --name $IMAGE_NAME -d -p 3030:3030 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge $CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG + - sleep 5 + - docker ps -a + - docker logs $IMAGE_NAME + - docker exec -i $IMAGE_NAME bash -c "coverage run -m pytest --log-level=INFO --verbose $IMAGE_NAME/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml; coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml; coverage report --include='${IMAGE_NAME}/*' --show-missing" + coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/' + after_script: + - docker rm -f $IMAGE_NAME + - docker network rm teraflowbridge + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)' + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"' + - changes: + - src/$IMAGE_NAME/**/*.{py,in,yml} + - src/$IMAGE_NAME/Dockerfile + - src/$IMAGE_NAME/tests/*.py + - src/$IMAGE_NAME/tests/Dockerfile + - manifests/${IMAGE_NAME}service.yaml + - .gitlab-ci.yml + artifacts: + when: always + reports: + junit: src/$IMAGE_NAME/tests/${IMAGE_NAME}_report.xml + cobertura: src/$IMAGE_NAME/tests/${IMAGE_NAME}_coverage.xml + +# Deployment of the service in Kubernetes Cluster +deploy service: + variables: + IMAGE_NAME: 'service' # name of the microservice + IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) + stage: deploy + needs: + - unit test service + # - integ_test execute + script: + - 'sed -i "s/$IMAGE_NAME:.*/$IMAGE_NAME:$IMAGE_TAG/" manifests/${IMAGE_NAME}service.yaml' + - kubectl version + - kubectl get all + - kubectl apply -f "manifests/${IMAGE_NAME}service.yaml" + - kubectl get all + # environment: + # name: test + # url: https://example.com + # kubernetes: + # namespace: test + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && ($CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == $CI_DEFAULT_BRANCH)' + when: manual + - if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH == "develop"' + when: manual diff --git a/src/interdomain/Config.py b/src/interdomain/Config.py new file mode 100644 index 000000000..b2e558ac1 --- /dev/null +++ b/src/interdomain/Config.py @@ -0,0 +1,18 @@ +import logging + +# General settings +LOG_LEVEL = logging.WARNING + +# gRPC settings +GRPC_INTERDOMAIN_PORT = 9090 +GRPC_MAX_WORKERS = 10 +GRPC_GRACE_PERIOD = 60 + +# Prometheus settings +METRICS_PORT = 9192 + +# Dependency micro-service connection settings +SLICE_SERVICE_HOST = '127.0.0.1' +SLICE_SERVICE_PORT = 1010 + + diff --git a/src/interdomain/Dockerfile b/src/interdomain/Dockerfile new file mode 100644 index 000000000..26438173c --- /dev/null +++ b/src/interdomain/Dockerfile @@ -0,0 +1,39 @@ +FROM python:3-slim + +# Install dependencies +RUN apt-get --yes --quiet --quiet update && \ + apt-get --yes --quiet --quiet install wget g++ && \ + rm -rf /var/lib/apt/lists/* + +# Set Python to show logs as they occur +ENV PYTHONUNBUFFERED=0 + +# Download the gRPC health probe +RUN GRPC_HEALTH_PROBE_VERSION=v0.2.0 && \ + wget -qO/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-amd64 && \ + chmod +x /bin/grpc_health_probe + +# Get generic Python packages +RUN python3 -m pip install --upgrade pip setuptools wheel pip-tools + +# Set working directory +WORKDIR /var/teraflow + +# Create module sub-folders +RUN mkdir -p /var/teraflow/interdomain + +# Get Python packages per module +COPY interdomain/requirements.in interdomain/requirements.in +RUN pip-compile --output-file=interdomain/requirements.txt interdomain/requirements.in +RUN python3 -m pip install -r interdomain/requirements.in + +# Add files into working directory +COPY common/. common +COPY context/. context +COPY device/. device +COPY monitoring/. monitoring +COPY service/. service +COPY interdomain/. interdomain + +# Start service interdomain +ENTRYPOINT ["python", "-m", "service.interdomain"] diff --git a/src/interdomain/__init__.py b/src/interdomain/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/interdomain/client/InterdomainClient.py b/src/interdomain/client/InterdomainClient.py new file mode 100644 index 000000000..be226f53a --- /dev/null +++ b/src/interdomain/client/InterdomainClient.py @@ -0,0 +1,55 @@ +import grpc, logging +from common.tools.client.RetryDecorator import retry, delay_exponential +from interdomain.proto.context_pb2 import TeraFlowController, AuthenticationResult +from interdomain.proto.slice_pb2 import TransportSlice, SliceId +from interdomain.proto.interdomain_pb2_grpc import InterdomainServiceStub + +LOGGER = logging.getLogger(__name__) +MAX_RETRIES = 15 +DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0) + +class InterdomainClient: + def __init__(self, address, port): + self.endpoint = '{:s}:{:s}'.format(str(address), str(port)) + LOGGER.debug('Creating channel to {:s}...'.format(self.endpoint)) + self.channel = None + self.stub = None + self.connect() + LOGGER.debug('Channel created') + + def connect(self): + self.channel = grpc.insecure_channel(self.endpoint) + self.stub = InterdomainServiceStub(self.channel) + + def close(self): + if self.channel is not None: self.channel.close() + self.channel = None + self.stub = None + + @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') + def Authenticate(self, request : TeraFlowController) -> AuthenticationResult: + LOGGER.debug('Authenticate request: {:s}'.format(str(request))) + response = self.stub.Authenticate(request) + LOGGER.debug('Authenticate result: {:s}'.format(str(response))) + return response + + @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') + def LookUpSlice(self, request : TransportSlice) -> SliceId: + LOGGER.debug('LookUpSlice request: {:s}'.format(str(request))) + response = self.stub.LookUpSlice(request) + LOGGER.debug('LookUpSlice result: {:s}'.format(str(response))) + return response + + @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') + def OrderSliceFromCatalog(self, request : TransportSlice) -> SliceStatus: + LOGGER.debug('OrderSliceFromCatalog request: {:s}'.format(str(request))) + response = self.stub.OrderSliceFromCatalog(request) + LOGGER.debug('OrderSliceFromCatalog result: {:s}'.format(str(response))) + return response + + @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') + def CreateSliceAndAddToCatalog(self, request : TransportSlice) -> SliceStatus: + LOGGER.debug('CreateSliceAndAddToCatalog request: {:s}'.format(str(request))) + response = self.stub.CreateSliceAndAddToCatalog(request) + LOGGER.debug('CreateSliceAndAddToCatalog result: {:s}'.format(str(response))) + return response diff --git a/src/interdomain/client/__init__.py b/src/interdomain/client/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/interdomain/genproto.sh b/src/interdomain/genproto.sh new file mode 100755 index 000000000..e85797797 --- /dev/null +++ b/src/interdomain/genproto.sh @@ -0,0 +1,37 @@ +#!/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 interdomain.proto + +rm proto/context_pb2_grpc.py +rm proto/interdomain_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/service_pb2_grpc.py +sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/interdomain_pb2.py + diff --git a/src/interdomain/proto/__init__.py b/src/interdomain/proto/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/interdomain/proto/context_pb2.py b/src/interdomain/proto/context_pb2.py new file mode 100644 index 000000000..68602b16f --- /dev/null +++ b/src/interdomain/proto/context_pb2.py @@ -0,0 +1,2662 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: context.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 kpi_sample_types_pb2 as kpi__sample__types__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='context.proto', + package='context', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + , + dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) + +_EVENTTYPEENUM = _descriptor.EnumDescriptor( + name='EventTypeEnum', + full_name='context.EventTypeEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_CREATE', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_UPDATE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_REMOVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3703, + serialized_end=3809, +) +_sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) + +EventTypeEnum = enum_type_wrapper.EnumTypeWrapper(_EVENTTYPEENUM) +_DEVICEDRIVERENUM = _descriptor.EnumDescriptor( + name='DeviceDriverEnum', + full_name='context.DeviceDriverEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_OPENCONFIG', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_TRANSPORT_API', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_P4', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_IETF_NETWORK_TOPOLOGY', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_ONF_TR_352', index=5, number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3812, + serialized_end=4009, +) +_sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) + +DeviceDriverEnum = enum_type_wrapper.EnumTypeWrapper(_DEVICEDRIVERENUM) +_DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( + name='DeviceOperationalStatusEnum', + full_name='context.DeviceOperationalStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DEVICEOPERATIONALSTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEOPERATIONALSTATUS_DISABLED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEOPERATIONALSTATUS_ENABLED', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4012, + serialized_end=4155, +) +_sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) + +DeviceOperationalStatusEnum = enum_type_wrapper.EnumTypeWrapper(_DEVICEOPERATIONALSTATUSENUM) +_SERVICETYPEENUM = _descriptor.EnumDescriptor( + name='ServiceTypeEnum', + full_name='context.ServiceTypeEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_L3NM', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_L2NM', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_TAPI_CONNECTIVITY_SERVICE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4158, + serialized_end=4287, +) +_sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) + +ServiceTypeEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICETYPEENUM) +_SERVICESTATUSENUM = _descriptor.EnumDescriptor( + name='ServiceStatusEnum', + full_name='context.ServiceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_ACTIVE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_PENDING_REMOVAL', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4290, + serialized_end=4426, +) +_sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) + +ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_CONFIGACTIONENUM = _descriptor.EnumDescriptor( + name='ConfigActionEnum', + full_name='context.ConfigActionEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='CONFIGACTION_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CONFIGACTION_SET', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CONFIGACTION_DELETE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4428, + serialized_end=4521, +) +_sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) + +ConfigActionEnum = enum_type_wrapper.EnumTypeWrapper(_CONFIGACTIONENUM) +EVENTTYPE_UNDEFINED = 0 +EVENTTYPE_CREATE = 1 +EVENTTYPE_UPDATE = 2 +EVENTTYPE_REMOVE = 3 +DEVICEDRIVER_UNDEFINED = 0 +DEVICEDRIVER_OPENCONFIG = 1 +DEVICEDRIVER_TRANSPORT_API = 2 +DEVICEDRIVER_P4 = 3 +DEVICEDRIVER_IETF_NETWORK_TOPOLOGY = 4 +DEVICEDRIVER_ONF_TR_352 = 5 +DEVICEOPERATIONALSTATUS_UNDEFINED = 0 +DEVICEOPERATIONALSTATUS_DISABLED = 1 +DEVICEOPERATIONALSTATUS_ENABLED = 2 +SERVICETYPE_UNKNOWN = 0 +SERVICETYPE_L3NM = 1 +SERVICETYPE_L2NM = 2 +SERVICETYPE_TAPI_CONNECTIVITY_SERVICE = 3 +SERVICESTATUS_UNDEFINED = 0 +SERVICESTATUS_PLANNED = 1 +SERVICESTATUS_ACTIVE = 2 +SERVICESTATUS_PENDING_REMOVAL = 3 +CONFIGACTION_UNDEFINED = 0 +CONFIGACTION_SET = 1 +CONFIGACTION_DELETE = 2 + + + +_EMPTY = _descriptor.Descriptor( + name='Empty', + full_name='context.Empty', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=50, + serialized_end=57, +) + + +_UUID = _descriptor.Descriptor( + name='Uuid', + full_name='context.Uuid', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='uuid', full_name='context.Uuid.uuid', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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=59, + serialized_end=79, +) + + +_EVENT = _descriptor.Descriptor( + name='Event', + full_name='context.Event', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='timestamp', full_name='context.Event.timestamp', index=0, + number=1, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(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), + _descriptor.FieldDescriptor( + name='event_type', full_name='context.Event.event_type', 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=81, + serialized_end=151, +) + + +_CONTEXTID = _descriptor.Descriptor( + name='ContextId', + full_name='context.ContextId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_uuid', full_name='context.ContextId.context_uuid', 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=153, + serialized_end=201, +) + + +_CONTEXT = _descriptor.Descriptor( + name='Context', + full_name='context.Context', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.Context.context_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='topology_ids', full_name='context.Context.topology_ids', 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='service_ids', full_name='context.Context.service_ids', 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='controller', full_name='context.Context.controller', index=3, + number=4, 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=204, + serialized_end=386, +) + + +_CONTEXTIDLIST = _descriptor.Descriptor( + name='ContextIdList', + full_name='context.ContextIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_ids', full_name='context.ContextIdList.context_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=388, + serialized_end=444, +) + + +_CONTEXTLIST = _descriptor.Descriptor( + name='ContextList', + full_name='context.ContextList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='contexts', full_name='context.ContextList.contexts', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=446, + serialized_end=495, +) + + +_CONTEXTEVENT = _descriptor.Descriptor( + name='ContextEvent', + full_name='context.ContextEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.ContextEvent.event', 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='context_id', full_name='context.ContextEvent.context_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=497, + serialized_end=582, +) + + +_TOPOLOGYID = _descriptor.Descriptor( + name='TopologyId', + full_name='context.TopologyId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.TopologyId.context_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='topology_uuid', full_name='context.TopologyId.topology_uuid', 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=584, + serialized_end=674, +) + + +_TOPOLOGY = _descriptor.Descriptor( + name='Topology', + full_name='context.Topology', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topology_id', full_name='context.Topology.topology_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='device_ids', full_name='context.Topology.device_ids', 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='link_ids', full_name='context.Topology.link_ids', 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=676, + serialized_end=802, +) + + +_TOPOLOGYIDLIST = _descriptor.Descriptor( + name='TopologyIdList', + full_name='context.TopologyIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topology_ids', full_name='context.TopologyIdList.topology_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=804, + serialized_end=863, +) + + +_TOPOLOGYLIST = _descriptor.Descriptor( + name='TopologyList', + full_name='context.TopologyList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topologies', full_name='context.TopologyList.topologies', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=865, + serialized_end=918, +) + + +_TOPOLOGYEVENT = _descriptor.Descriptor( + name='TopologyEvent', + full_name='context.TopologyEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.TopologyEvent.event', 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='topology_id', full_name='context.TopologyEvent.topology_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=920, + serialized_end=1008, +) + + +_DEVICEID = _descriptor.Descriptor( + name='DeviceId', + full_name='context.DeviceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='device_uuid', full_name='context.DeviceId.device_uuid', 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=1010, + serialized_end=1056, +) + + +_DEVICE = _descriptor.Descriptor( + name='Device', + full_name='context.Device', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='device_id', full_name='context.Device.device_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='device_type', full_name='context.Device.device_type', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='device_config', full_name='context.Device.device_config', index=2, + number=3, 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='device_operational_status', full_name='context.Device.device_operational_status', index=3, + number=4, 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), + _descriptor.FieldDescriptor( + name='device_drivers', full_name='context.Device.device_drivers', index=4, + number=5, type=14, cpp_type=8, 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='device_endpoints', full_name='context.Device.device_endpoints', index=5, + number=6, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1059, + serialized_end=1341, +) + + +_DEVICECONFIG = _descriptor.Descriptor( + name='DeviceConfig', + full_name='context.DeviceConfig', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='config_rules', full_name='context.DeviceConfig.config_rules', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1343, + serialized_end=1400, +) + + +_DEVICEIDLIST = _descriptor.Descriptor( + name='DeviceIdList', + full_name='context.DeviceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='device_ids', full_name='context.DeviceIdList.device_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1402, + serialized_end=1455, +) + + +_DEVICELIST = _descriptor.Descriptor( + name='DeviceList', + full_name='context.DeviceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='devices', full_name='context.DeviceList.devices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1457, + serialized_end=1503, +) + + +_DEVICEEVENT = _descriptor.Descriptor( + name='DeviceEvent', + full_name='context.DeviceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.DeviceEvent.event', 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='device_id', full_name='context.DeviceEvent.device_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=1505, + serialized_end=1587, +) + + +_LINKID = _descriptor.Descriptor( + name='LinkId', + full_name='context.LinkId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='link_uuid', full_name='context.LinkId.link_uuid', 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=1589, + serialized_end=1631, +) + + +_LINK = _descriptor.Descriptor( + name='Link', + full_name='context.Link', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='link_id', full_name='context.Link.link_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='link_endpoint_ids', full_name='context.Link.link_endpoint_ids', 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1633, + serialized_end=1721, +) + + +_LINKIDLIST = _descriptor.Descriptor( + name='LinkIdList', + full_name='context.LinkIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='link_ids', full_name='context.LinkIdList.link_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1723, + serialized_end=1770, +) + + +_LINKLIST = _descriptor.Descriptor( + name='LinkList', + full_name='context.LinkList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='links', full_name='context.LinkList.links', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1772, + serialized_end=1812, +) + + +_LINKEVENT = _descriptor.Descriptor( + name='LinkEvent', + full_name='context.LinkEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.LinkEvent.event', 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='link_id', full_name='context.LinkEvent.link_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=1814, + serialized_end=1890, +) + + +_SERVICEID = _descriptor.Descriptor( + name='ServiceId', + full_name='context.ServiceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.ServiceId.context_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='service_uuid', full_name='context.ServiceId.service_uuid', 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=1892, + serialized_end=1980, +) + + +_SERVICE = _descriptor.Descriptor( + name='Service', + full_name='context.Service', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='service_id', full_name='context.Service.service_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='service_type', full_name='context.Service.service_type', 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), + _descriptor.FieldDescriptor( + name='service_endpoint_ids', full_name='context.Service.service_endpoint_ids', 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='service_constraints', full_name='context.Service.service_constraints', 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='service_status', full_name='context.Service.service_status', index=4, + number=5, 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='service_config', full_name='context.Service.service_config', 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=1983, + serialized_end=2277, +) + + +_SERVICESTATUS = _descriptor.Descriptor( + name='ServiceStatus', + full_name='context.ServiceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='service_status', full_name='context.ServiceStatus.service_status', index=0, + number=1, 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=2279, + serialized_end=2346, +) + + +_SERVICECONFIG = _descriptor.Descriptor( + name='ServiceConfig', + full_name='context.ServiceConfig', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='config_rules', full_name='context.ServiceConfig.config_rules', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2348, + serialized_end=2406, +) + + +_SERVICEIDLIST = _descriptor.Descriptor( + name='ServiceIdList', + full_name='context.ServiceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='service_ids', full_name='context.ServiceIdList.service_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2408, + serialized_end=2464, +) + + +_SERVICELIST = _descriptor.Descriptor( + name='ServiceList', + full_name='context.ServiceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='services', full_name='context.ServiceList.services', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2466, + serialized_end=2515, +) + + +_SERVICEEVENT = _descriptor.Descriptor( + name='ServiceEvent', + full_name='context.ServiceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.ServiceEvent.event', 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='service_id', full_name='context.ServiceEvent.service_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=2517, + serialized_end=2602, +) + + +_CONNECTIONID = _descriptor.Descriptor( + name='ConnectionId', + full_name='context.ConnectionId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connection_uuid', full_name='context.ConnectionId.connection_uuid', 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=2604, + serialized_end=2658, +) + + +_CONNECTION = _descriptor.Descriptor( + name='Connection', + full_name='context.Connection', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connection_id', full_name='context.Connection.connection_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='service_id', full_name='context.Connection.service_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), + _descriptor.FieldDescriptor( + name='path_hops_endpoint_ids', full_name='context.Connection.path_hops_endpoint_ids', 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='sub_service_ids', full_name='context.Connection.sub_service_ids', 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2661, + serialized_end=2857, +) + + +_CONNECTIONIDLIST = _descriptor.Descriptor( + name='ConnectionIdList', + full_name='context.ConnectionIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connection_ids', full_name='context.ConnectionIdList.connection_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2859, + serialized_end=2924, +) + + +_CONNECTIONLIST = _descriptor.Descriptor( + name='ConnectionList', + full_name='context.ConnectionList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connections', full_name='context.ConnectionList.connections', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2926, + serialized_end=2984, +) + + +_CONNECTIONEVENT = _descriptor.Descriptor( + name='ConnectionEvent', + full_name='context.ConnectionEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.ConnectionEvent.event', 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='connection_id', full_name='context.ConnectionEvent.connection_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=2986, + serialized_end=3080, +) + + +_ENDPOINTID = _descriptor.Descriptor( + name='EndPointId', + full_name='context.EndPointId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topology_id', full_name='context.EndPointId.topology_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='device_id', full_name='context.EndPointId.device_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), + _descriptor.FieldDescriptor( + name='endpoint_uuid', full_name='context.EndPointId.endpoint_uuid', index=2, + number=3, 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=3083, + serialized_end=3213, +) + + +_ENDPOINT = _descriptor.Descriptor( + name='EndPoint', + full_name='context.EndPoint', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='endpoint_id', full_name='context.EndPoint.endpoint_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='endpoint_type', full_name='context.EndPoint.endpoint_type', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='kpi_sample_types', full_name='context.EndPoint.kpi_sample_types', index=2, + number=3, type=14, cpp_type=8, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3216, + serialized_end=3350, +) + + +_CONFIGRULE = _descriptor.Descriptor( + name='ConfigRule', + full_name='context.ConfigRule', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='action', full_name='context.ConfigRule.action', index=0, + number=1, 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), + _descriptor.FieldDescriptor( + name='resource_key', full_name='context.ConfigRule.resource_key', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='resource_value', full_name='context.ConfigRule.resource_value', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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=3352, + serialized_end=3453, +) + + +_CONSTRAINT = _descriptor.Descriptor( + name='Constraint', + full_name='context.Constraint', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='constraint_type', full_name='context.Constraint.constraint_type', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='constraint_value', full_name='context.Constraint.constraint_value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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=3455, + serialized_end=3518, +) + + +_TERAFLOWCONTROLLER = _descriptor.Descriptor( + name='TeraFlowController', + full_name='context.TeraFlowController', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.TeraFlowController.context_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='ip_address', full_name='context.TeraFlowController.ip_address', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='port', full_name='context.TeraFlowController.port', index=2, + number=3, type=13, cpp_type=3, 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=3520, + serialized_end=3614, +) + + +_AUTHENTICATIONRESULT = _descriptor.Descriptor( + name='AuthenticationResult', + full_name='context.AuthenticationResult', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.AuthenticationResult.context_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='authenticated', full_name='context.AuthenticationResult.authenticated', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + 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=3616, + serialized_end=3701, +) + +_EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM +_CONTEXTID.fields_by_name['context_uuid'].message_type = _UUID +_CONTEXT.fields_by_name['context_id'].message_type = _CONTEXTID +_CONTEXT.fields_by_name['topology_ids'].message_type = _TOPOLOGYID +_CONTEXT.fields_by_name['service_ids'].message_type = _SERVICEID +_CONTEXT.fields_by_name['controller'].message_type = _TERAFLOWCONTROLLER +_CONTEXTIDLIST.fields_by_name['context_ids'].message_type = _CONTEXTID +_CONTEXTLIST.fields_by_name['contexts'].message_type = _CONTEXT +_CONTEXTEVENT.fields_by_name['event'].message_type = _EVENT +_CONTEXTEVENT.fields_by_name['context_id'].message_type = _CONTEXTID +_TOPOLOGYID.fields_by_name['context_id'].message_type = _CONTEXTID +_TOPOLOGYID.fields_by_name['topology_uuid'].message_type = _UUID +_TOPOLOGY.fields_by_name['topology_id'].message_type = _TOPOLOGYID +_TOPOLOGY.fields_by_name['device_ids'].message_type = _DEVICEID +_TOPOLOGY.fields_by_name['link_ids'].message_type = _LINKID +_TOPOLOGYIDLIST.fields_by_name['topology_ids'].message_type = _TOPOLOGYID +_TOPOLOGYLIST.fields_by_name['topologies'].message_type = _TOPOLOGY +_TOPOLOGYEVENT.fields_by_name['event'].message_type = _EVENT +_TOPOLOGYEVENT.fields_by_name['topology_id'].message_type = _TOPOLOGYID +_DEVICEID.fields_by_name['device_uuid'].message_type = _UUID +_DEVICE.fields_by_name['device_id'].message_type = _DEVICEID +_DEVICE.fields_by_name['device_config'].message_type = _DEVICECONFIG +_DEVICE.fields_by_name['device_operational_status'].enum_type = _DEVICEOPERATIONALSTATUSENUM +_DEVICE.fields_by_name['device_drivers'].enum_type = _DEVICEDRIVERENUM +_DEVICE.fields_by_name['device_endpoints'].message_type = _ENDPOINT +_DEVICECONFIG.fields_by_name['config_rules'].message_type = _CONFIGRULE +_DEVICEIDLIST.fields_by_name['device_ids'].message_type = _DEVICEID +_DEVICELIST.fields_by_name['devices'].message_type = _DEVICE +_DEVICEEVENT.fields_by_name['event'].message_type = _EVENT +_DEVICEEVENT.fields_by_name['device_id'].message_type = _DEVICEID +_LINKID.fields_by_name['link_uuid'].message_type = _UUID +_LINK.fields_by_name['link_id'].message_type = _LINKID +_LINK.fields_by_name['link_endpoint_ids'].message_type = _ENDPOINTID +_LINKIDLIST.fields_by_name['link_ids'].message_type = _LINKID +_LINKLIST.fields_by_name['links'].message_type = _LINK +_LINKEVENT.fields_by_name['event'].message_type = _EVENT +_LINKEVENT.fields_by_name['link_id'].message_type = _LINKID +_SERVICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SERVICEID.fields_by_name['service_uuid'].message_type = _UUID +_SERVICE.fields_by_name['service_id'].message_type = _SERVICEID +_SERVICE.fields_by_name['service_type'].enum_type = _SERVICETYPEENUM +_SERVICE.fields_by_name['service_endpoint_ids'].message_type = _ENDPOINTID +_SERVICE.fields_by_name['service_constraints'].message_type = _CONSTRAINT +_SERVICE.fields_by_name['service_status'].message_type = _SERVICESTATUS +_SERVICE.fields_by_name['service_config'].message_type = _SERVICECONFIG +_SERVICESTATUS.fields_by_name['service_status'].enum_type = _SERVICESTATUSENUM +_SERVICECONFIG.fields_by_name['config_rules'].message_type = _CONFIGRULE +_SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID +_SERVICELIST.fields_by_name['services'].message_type = _SERVICE +_SERVICEEVENT.fields_by_name['event'].message_type = _EVENT +_SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID +_CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID +_CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID +_CONNECTION.fields_by_name['path_hops_endpoint_ids'].message_type = _ENDPOINTID +_CONNECTION.fields_by_name['sub_service_ids'].message_type = _SERVICEID +_CONNECTIONIDLIST.fields_by_name['connection_ids'].message_type = _CONNECTIONID +_CONNECTIONLIST.fields_by_name['connections'].message_type = _CONNECTION +_CONNECTIONEVENT.fields_by_name['event'].message_type = _EVENT +_CONNECTIONEVENT.fields_by_name['connection_id'].message_type = _CONNECTIONID +_ENDPOINTID.fields_by_name['topology_id'].message_type = _TOPOLOGYID +_ENDPOINTID.fields_by_name['device_id'].message_type = _DEVICEID +_ENDPOINTID.fields_by_name['endpoint_uuid'].message_type = _UUID +_ENDPOINT.fields_by_name['endpoint_id'].message_type = _ENDPOINTID +_ENDPOINT.fields_by_name['kpi_sample_types'].enum_type = kpi__sample__types__pb2._KPISAMPLETYPE +_CONFIGRULE.fields_by_name['action'].enum_type = _CONFIGACTIONENUM +_TERAFLOWCONTROLLER.fields_by_name['context_id'].message_type = _CONTEXTID +_AUTHENTICATIONRESULT.fields_by_name['context_id'].message_type = _CONTEXTID +DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY +DESCRIPTOR.message_types_by_name['Uuid'] = _UUID +DESCRIPTOR.message_types_by_name['Event'] = _EVENT +DESCRIPTOR.message_types_by_name['ContextId'] = _CONTEXTID +DESCRIPTOR.message_types_by_name['Context'] = _CONTEXT +DESCRIPTOR.message_types_by_name['ContextIdList'] = _CONTEXTIDLIST +DESCRIPTOR.message_types_by_name['ContextList'] = _CONTEXTLIST +DESCRIPTOR.message_types_by_name['ContextEvent'] = _CONTEXTEVENT +DESCRIPTOR.message_types_by_name['TopologyId'] = _TOPOLOGYID +DESCRIPTOR.message_types_by_name['Topology'] = _TOPOLOGY +DESCRIPTOR.message_types_by_name['TopologyIdList'] = _TOPOLOGYIDLIST +DESCRIPTOR.message_types_by_name['TopologyList'] = _TOPOLOGYLIST +DESCRIPTOR.message_types_by_name['TopologyEvent'] = _TOPOLOGYEVENT +DESCRIPTOR.message_types_by_name['DeviceId'] = _DEVICEID +DESCRIPTOR.message_types_by_name['Device'] = _DEVICE +DESCRIPTOR.message_types_by_name['DeviceConfig'] = _DEVICECONFIG +DESCRIPTOR.message_types_by_name['DeviceIdList'] = _DEVICEIDLIST +DESCRIPTOR.message_types_by_name['DeviceList'] = _DEVICELIST +DESCRIPTOR.message_types_by_name['DeviceEvent'] = _DEVICEEVENT +DESCRIPTOR.message_types_by_name['LinkId'] = _LINKID +DESCRIPTOR.message_types_by_name['Link'] = _LINK +DESCRIPTOR.message_types_by_name['LinkIdList'] = _LINKIDLIST +DESCRIPTOR.message_types_by_name['LinkList'] = _LINKLIST +DESCRIPTOR.message_types_by_name['LinkEvent'] = _LINKEVENT +DESCRIPTOR.message_types_by_name['ServiceId'] = _SERVICEID +DESCRIPTOR.message_types_by_name['Service'] = _SERVICE +DESCRIPTOR.message_types_by_name['ServiceStatus'] = _SERVICESTATUS +DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG +DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST +DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST +DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID +DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION +DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST +DESCRIPTOR.message_types_by_name['ConnectionList'] = _CONNECTIONLIST +DESCRIPTOR.message_types_by_name['ConnectionEvent'] = _CONNECTIONEVENT +DESCRIPTOR.message_types_by_name['EndPointId'] = _ENDPOINTID +DESCRIPTOR.message_types_by_name['EndPoint'] = _ENDPOINT +DESCRIPTOR.message_types_by_name['ConfigRule'] = _CONFIGRULE +DESCRIPTOR.message_types_by_name['Constraint'] = _CONSTRAINT +DESCRIPTOR.message_types_by_name['TeraFlowController'] = _TERAFLOWCONTROLLER +DESCRIPTOR.message_types_by_name['AuthenticationResult'] = _AUTHENTICATIONRESULT +DESCRIPTOR.enum_types_by_name['EventTypeEnum'] = _EVENTTYPEENUM +DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM +DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM +DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM +DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), { + 'DESCRIPTOR' : _EMPTY, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Empty) + }) +_sym_db.RegisterMessage(Empty) + +Uuid = _reflection.GeneratedProtocolMessageType('Uuid', (_message.Message,), { + 'DESCRIPTOR' : _UUID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Uuid) + }) +_sym_db.RegisterMessage(Uuid) + +Event = _reflection.GeneratedProtocolMessageType('Event', (_message.Message,), { + 'DESCRIPTOR' : _EVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Event) + }) +_sym_db.RegisterMessage(Event) + +ContextId = _reflection.GeneratedProtocolMessageType('ContextId', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextId) + }) +_sym_db.RegisterMessage(ContextId) + +Context = _reflection.GeneratedProtocolMessageType('Context', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Context) + }) +_sym_db.RegisterMessage(Context) + +ContextIdList = _reflection.GeneratedProtocolMessageType('ContextIdList', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextIdList) + }) +_sym_db.RegisterMessage(ContextIdList) + +ContextList = _reflection.GeneratedProtocolMessageType('ContextList', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextList) + }) +_sym_db.RegisterMessage(ContextList) + +ContextEvent = _reflection.GeneratedProtocolMessageType('ContextEvent', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextEvent) + }) +_sym_db.RegisterMessage(ContextEvent) + +TopologyId = _reflection.GeneratedProtocolMessageType('TopologyId', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyId) + }) +_sym_db.RegisterMessage(TopologyId) + +Topology = _reflection.GeneratedProtocolMessageType('Topology', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGY, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Topology) + }) +_sym_db.RegisterMessage(Topology) + +TopologyIdList = _reflection.GeneratedProtocolMessageType('TopologyIdList', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyIdList) + }) +_sym_db.RegisterMessage(TopologyIdList) + +TopologyList = _reflection.GeneratedProtocolMessageType('TopologyList', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyList) + }) +_sym_db.RegisterMessage(TopologyList) + +TopologyEvent = _reflection.GeneratedProtocolMessageType('TopologyEvent', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyEvent) + }) +_sym_db.RegisterMessage(TopologyEvent) + +DeviceId = _reflection.GeneratedProtocolMessageType('DeviceId', (_message.Message,), { + 'DESCRIPTOR' : _DEVICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceId) + }) +_sym_db.RegisterMessage(DeviceId) + +Device = _reflection.GeneratedProtocolMessageType('Device', (_message.Message,), { + 'DESCRIPTOR' : _DEVICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Device) + }) +_sym_db.RegisterMessage(Device) + +DeviceConfig = _reflection.GeneratedProtocolMessageType('DeviceConfig', (_message.Message,), { + 'DESCRIPTOR' : _DEVICECONFIG, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceConfig) + }) +_sym_db.RegisterMessage(DeviceConfig) + +DeviceIdList = _reflection.GeneratedProtocolMessageType('DeviceIdList', (_message.Message,), { + 'DESCRIPTOR' : _DEVICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceIdList) + }) +_sym_db.RegisterMessage(DeviceIdList) + +DeviceList = _reflection.GeneratedProtocolMessageType('DeviceList', (_message.Message,), { + 'DESCRIPTOR' : _DEVICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceList) + }) +_sym_db.RegisterMessage(DeviceList) + +DeviceEvent = _reflection.GeneratedProtocolMessageType('DeviceEvent', (_message.Message,), { + 'DESCRIPTOR' : _DEVICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceEvent) + }) +_sym_db.RegisterMessage(DeviceEvent) + +LinkId = _reflection.GeneratedProtocolMessageType('LinkId', (_message.Message,), { + 'DESCRIPTOR' : _LINKID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkId) + }) +_sym_db.RegisterMessage(LinkId) + +Link = _reflection.GeneratedProtocolMessageType('Link', (_message.Message,), { + 'DESCRIPTOR' : _LINK, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Link) + }) +_sym_db.RegisterMessage(Link) + +LinkIdList = _reflection.GeneratedProtocolMessageType('LinkIdList', (_message.Message,), { + 'DESCRIPTOR' : _LINKIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkIdList) + }) +_sym_db.RegisterMessage(LinkIdList) + +LinkList = _reflection.GeneratedProtocolMessageType('LinkList', (_message.Message,), { + 'DESCRIPTOR' : _LINKLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkList) + }) +_sym_db.RegisterMessage(LinkList) + +LinkEvent = _reflection.GeneratedProtocolMessageType('LinkEvent', (_message.Message,), { + 'DESCRIPTOR' : _LINKEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkEvent) + }) +_sym_db.RegisterMessage(LinkEvent) + +ServiceId = _reflection.GeneratedProtocolMessageType('ServiceId', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceId) + }) +_sym_db.RegisterMessage(ServiceId) + +Service = _reflection.GeneratedProtocolMessageType('Service', (_message.Message,), { + 'DESCRIPTOR' : _SERVICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Service) + }) +_sym_db.RegisterMessage(Service) + +ServiceStatus = _reflection.GeneratedProtocolMessageType('ServiceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SERVICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceStatus) + }) +_sym_db.RegisterMessage(ServiceStatus) + +ServiceConfig = _reflection.GeneratedProtocolMessageType('ServiceConfig', (_message.Message,), { + 'DESCRIPTOR' : _SERVICECONFIG, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceConfig) + }) +_sym_db.RegisterMessage(ServiceConfig) + +ServiceIdList = _reflection.GeneratedProtocolMessageType('ServiceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceIdList) + }) +_sym_db.RegisterMessage(ServiceIdList) + +ServiceList = _reflection.GeneratedProtocolMessageType('ServiceList', (_message.Message,), { + 'DESCRIPTOR' : _SERVICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceList) + }) +_sym_db.RegisterMessage(ServiceList) + +ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceEvent) + }) +_sym_db.RegisterMessage(ServiceEvent) + +ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionId) + }) +_sym_db.RegisterMessage(ConnectionId) + +Connection = _reflection.GeneratedProtocolMessageType('Connection', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTION, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Connection) + }) +_sym_db.RegisterMessage(Connection) + +ConnectionIdList = _reflection.GeneratedProtocolMessageType('ConnectionIdList', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionIdList) + }) +_sym_db.RegisterMessage(ConnectionIdList) + +ConnectionList = _reflection.GeneratedProtocolMessageType('ConnectionList', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionList) + }) +_sym_db.RegisterMessage(ConnectionList) + +ConnectionEvent = _reflection.GeneratedProtocolMessageType('ConnectionEvent', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionEvent) + }) +_sym_db.RegisterMessage(ConnectionEvent) + +EndPointId = _reflection.GeneratedProtocolMessageType('EndPointId', (_message.Message,), { + 'DESCRIPTOR' : _ENDPOINTID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.EndPointId) + }) +_sym_db.RegisterMessage(EndPointId) + +EndPoint = _reflection.GeneratedProtocolMessageType('EndPoint', (_message.Message,), { + 'DESCRIPTOR' : _ENDPOINT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.EndPoint) + }) +_sym_db.RegisterMessage(EndPoint) + +ConfigRule = _reflection.GeneratedProtocolMessageType('ConfigRule', (_message.Message,), { + 'DESCRIPTOR' : _CONFIGRULE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConfigRule) + }) +_sym_db.RegisterMessage(ConfigRule) + +Constraint = _reflection.GeneratedProtocolMessageType('Constraint', (_message.Message,), { + 'DESCRIPTOR' : _CONSTRAINT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Constraint) + }) +_sym_db.RegisterMessage(Constraint) + +TeraFlowController = _reflection.GeneratedProtocolMessageType('TeraFlowController', (_message.Message,), { + 'DESCRIPTOR' : _TERAFLOWCONTROLLER, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TeraFlowController) + }) +_sym_db.RegisterMessage(TeraFlowController) + +AuthenticationResult = _reflection.GeneratedProtocolMessageType('AuthenticationResult', (_message.Message,), { + 'DESCRIPTOR' : _AUTHENTICATIONRESULT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.AuthenticationResult) + }) +_sym_db.RegisterMessage(AuthenticationResult) + + + +_CONTEXTSERVICE = _descriptor.ServiceDescriptor( + name='ContextService', + full_name='context.ContextService', + file=DESCRIPTOR, + index=0, + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_start=4524, + serialized_end=6617, + methods=[ + _descriptor.MethodDescriptor( + name='ListContextIds', + full_name='context.ContextService.ListContextIds', + index=0, + containing_service=None, + input_type=_EMPTY, + output_type=_CONTEXTIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListContexts', + full_name='context.ContextService.ListContexts', + index=1, + containing_service=None, + input_type=_EMPTY, + output_type=_CONTEXTLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetContext', + full_name='context.ContextService.GetContext', + index=2, + containing_service=None, + input_type=_CONTEXTID, + output_type=_CONTEXT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetContext', + full_name='context.ContextService.SetContext', + index=3, + containing_service=None, + input_type=_CONTEXT, + output_type=_CONTEXTID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveContext', + full_name='context.ContextService.RemoveContext', + index=4, + containing_service=None, + input_type=_CONTEXTID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetContextEvents', + full_name='context.ContextService.GetContextEvents', + index=5, + containing_service=None, + input_type=_EMPTY, + output_type=_CONTEXTEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListTopologyIds', + full_name='context.ContextService.ListTopologyIds', + index=6, + containing_service=None, + input_type=_CONTEXTID, + output_type=_TOPOLOGYIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListTopologies', + full_name='context.ContextService.ListTopologies', + index=7, + containing_service=None, + input_type=_CONTEXTID, + output_type=_TOPOLOGYLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetTopology', + full_name='context.ContextService.GetTopology', + index=8, + containing_service=None, + input_type=_TOPOLOGYID, + output_type=_TOPOLOGY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetTopology', + full_name='context.ContextService.SetTopology', + index=9, + containing_service=None, + input_type=_TOPOLOGY, + output_type=_TOPOLOGYID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveTopology', + full_name='context.ContextService.RemoveTopology', + index=10, + containing_service=None, + input_type=_TOPOLOGYID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetTopologyEvents', + full_name='context.ContextService.GetTopologyEvents', + index=11, + containing_service=None, + input_type=_EMPTY, + output_type=_TOPOLOGYEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListDeviceIds', + full_name='context.ContextService.ListDeviceIds', + index=12, + containing_service=None, + input_type=_EMPTY, + output_type=_DEVICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListDevices', + full_name='context.ContextService.ListDevices', + index=13, + containing_service=None, + input_type=_EMPTY, + output_type=_DEVICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetDevice', + full_name='context.ContextService.GetDevice', + index=14, + containing_service=None, + input_type=_DEVICEID, + output_type=_DEVICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetDevice', + full_name='context.ContextService.SetDevice', + index=15, + containing_service=None, + input_type=_DEVICE, + output_type=_DEVICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveDevice', + full_name='context.ContextService.RemoveDevice', + index=16, + containing_service=None, + input_type=_DEVICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetDeviceEvents', + full_name='context.ContextService.GetDeviceEvents', + index=17, + containing_service=None, + input_type=_EMPTY, + output_type=_DEVICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListLinkIds', + full_name='context.ContextService.ListLinkIds', + index=18, + containing_service=None, + input_type=_EMPTY, + output_type=_LINKIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListLinks', + full_name='context.ContextService.ListLinks', + index=19, + containing_service=None, + input_type=_EMPTY, + output_type=_LINKLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetLink', + full_name='context.ContextService.GetLink', + index=20, + containing_service=None, + input_type=_LINKID, + output_type=_LINK, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetLink', + full_name='context.ContextService.SetLink', + index=21, + containing_service=None, + input_type=_LINK, + output_type=_LINKID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveLink', + full_name='context.ContextService.RemoveLink', + index=22, + containing_service=None, + input_type=_LINKID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetLinkEvents', + full_name='context.ContextService.GetLinkEvents', + index=23, + containing_service=None, + input_type=_EMPTY, + output_type=_LINKEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListServiceIds', + full_name='context.ContextService.ListServiceIds', + index=24, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SERVICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListServices', + full_name='context.ContextService.ListServices', + index=25, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SERVICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetService', + full_name='context.ContextService.GetService', + index=26, + containing_service=None, + input_type=_SERVICEID, + output_type=_SERVICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetService', + full_name='context.ContextService.SetService', + index=27, + containing_service=None, + input_type=_SERVICE, + output_type=_SERVICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveService', + full_name='context.ContextService.RemoveService', + index=28, + containing_service=None, + input_type=_SERVICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetServiceEvents', + full_name='context.ContextService.GetServiceEvents', + index=29, + containing_service=None, + input_type=_EMPTY, + output_type=_SERVICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListConnectionIds', + full_name='context.ContextService.ListConnectionIds', + index=30, + containing_service=None, + input_type=_SERVICEID, + output_type=_CONNECTIONIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListConnections', + full_name='context.ContextService.ListConnections', + index=31, + containing_service=None, + input_type=_SERVICEID, + output_type=_CONNECTIONLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetConnection', + full_name='context.ContextService.GetConnection', + index=32, + containing_service=None, + input_type=_CONNECTIONID, + output_type=_CONNECTION, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetConnection', + full_name='context.ContextService.SetConnection', + index=33, + containing_service=None, + input_type=_CONNECTION, + output_type=_CONNECTIONID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveConnection', + full_name='context.ContextService.RemoveConnection', + index=34, + containing_service=None, + input_type=_CONNECTIONID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetConnectionEvents', + full_name='context.ContextService.GetConnectionEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_CONNECTIONEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), +]) +_sym_db.RegisterServiceDescriptor(_CONTEXTSERVICE) + +DESCRIPTOR.services_by_name['ContextService'] = _CONTEXTSERVICE + +# @@protoc_insertion_point(module_scope) diff --git a/src/interdomain/proto/interdomain_pb2.py b/src/interdomain/proto/interdomain_pb2.py new file mode 100644 index 000000000..28abd7785 --- /dev/null +++ b/src/interdomain/proto/interdomain_pb2.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: interdomain.proto +"""Generated protocol buffer code.""" +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 slice_pb2 as slice__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='interdomain.proto', + package='interdomain', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x11interdomain.proto\x12\x0binterdomain\x1a\rcontext.proto\x1a\x0bslice.proto2\xab\x02\n\x12InterdomainService\x12L\n\x0c\x41uthenticate\x12\x1b.context.TeraFlowController\x1a\x1d.context.AuthenticationResult\"\x00\x12\x36\n\x0bLookUpSlice\x12\x15.slice.TransportSlice\x1a\x0e.slice.SliceId\"\x00\x12\x44\n\x15OrderSliceFromCatalog\x12\x15.slice.TransportSlice\x1a\x12.slice.SliceStatus\"\x00\x12I\n\x1a\x43reateSliceAndAddToCatalog\x12\x15.slice.TransportSlice\x1a\x12.slice.SliceStatus\"\x00\x62\x06proto3' + , + dependencies=[context__pb2.DESCRIPTOR,slice__pb2.DESCRIPTOR,]) + + + +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + +_INTERDOMAINSERVICE = _descriptor.ServiceDescriptor( + name='InterdomainService', + full_name='interdomain.InterdomainService', + file=DESCRIPTOR, + index=0, + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_start=63, + serialized_end=362, + methods=[ + _descriptor.MethodDescriptor( + name='Authenticate', + full_name='interdomain.InterdomainService.Authenticate', + index=0, + containing_service=None, + input_type=context__pb2._TERAFLOWCONTROLLER, + output_type=context__pb2._AUTHENTICATIONRESULT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='LookUpSlice', + full_name='interdomain.InterdomainService.LookUpSlice', + index=1, + containing_service=None, + input_type=slice__pb2._TRANSPORTSLICE, + output_type=slice__pb2._SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='OrderSliceFromCatalog', + full_name='interdomain.InterdomainService.OrderSliceFromCatalog', + index=2, + containing_service=None, + input_type=slice__pb2._TRANSPORTSLICE, + output_type=slice__pb2._SLICESTATUS, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='CreateSliceAndAddToCatalog', + full_name='interdomain.InterdomainService.CreateSliceAndAddToCatalog', + index=3, + containing_service=None, + input_type=slice__pb2._TRANSPORTSLICE, + output_type=slice__pb2._SLICESTATUS, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), +]) +_sym_db.RegisterServiceDescriptor(_INTERDOMAINSERVICE) + +DESCRIPTOR.services_by_name['InterdomainService'] = _INTERDOMAINSERVICE + +# @@protoc_insertion_point(module_scope) diff --git a/src/interdomain/proto/service_pb2.py b/src/interdomain/proto/service_pb2.py new file mode 100644 index 000000000..7a006915b --- /dev/null +++ b/src/interdomain/proto/service_pb2.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: service.proto +"""Generated protocol buffer code.""" +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 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='service.proto', + package='service', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xfd\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12\x42\n\x11GetConnectionList\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x62\x06proto3' + , + dependencies=[context__pb2.DESCRIPTOR,]) + + + +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + +_SERVICESERVICE = _descriptor.ServiceDescriptor( + name='ServiceService', + full_name='service.ServiceService', + file=DESCRIPTOR, + index=0, + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_start=42, + serialized_end=295, + methods=[ + _descriptor.MethodDescriptor( + name='CreateService', + full_name='service.ServiceService.CreateService', + index=0, + containing_service=None, + input_type=context__pb2._SERVICE, + output_type=context__pb2._SERVICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='UpdateService', + full_name='service.ServiceService.UpdateService', + index=1, + containing_service=None, + input_type=context__pb2._SERVICE, + output_type=context__pb2._SERVICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='DeleteService', + full_name='service.ServiceService.DeleteService', + index=2, + containing_service=None, + input_type=context__pb2._SERVICEID, + output_type=context__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetConnectionList', + full_name='service.ServiceService.GetConnectionList', + index=3, + containing_service=None, + input_type=context__pb2._SERVICEID, + output_type=context__pb2._CONNECTIONLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), +]) +_sym_db.RegisterServiceDescriptor(_SERVICESERVICE) + +DESCRIPTOR.services_by_name['ServiceService'] = _SERVICESERVICE + +# @@protoc_insertion_point(module_scope) diff --git a/src/interdomain/proto/service_pb2_grpc.py b/src/interdomain/proto/service_pb2_grpc.py new file mode 100644 index 000000000..58cd47e93 --- /dev/null +++ b/src/interdomain/proto/service_pb2_grpc.py @@ -0,0 +1,165 @@ +# 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 + + +class ServiceServiceStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.CreateService = channel.unary_unary( + '/service.ServiceService/CreateService', + request_serializer=context__pb2.Service.SerializeToString, + response_deserializer=context__pb2.ServiceId.FromString, + ) + self.UpdateService = channel.unary_unary( + '/service.ServiceService/UpdateService', + request_serializer=context__pb2.Service.SerializeToString, + response_deserializer=context__pb2.ServiceId.FromString, + ) + self.DeleteService = channel.unary_unary( + '/service.ServiceService/DeleteService', + request_serializer=context__pb2.ServiceId.SerializeToString, + response_deserializer=context__pb2.Empty.FromString, + ) + self.GetConnectionList = channel.unary_unary( + '/service.ServiceService/GetConnectionList', + request_serializer=context__pb2.ServiceId.SerializeToString, + response_deserializer=context__pb2.ConnectionList.FromString, + ) + + +class ServiceServiceServicer(object): + """Missing associated documentation comment in .proto file.""" + + def CreateService(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 UpdateService(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 DeleteService(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 GetConnectionList(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_ServiceServiceServicer_to_server(servicer, server): + rpc_method_handlers = { + 'CreateService': grpc.unary_unary_rpc_method_handler( + servicer.CreateService, + request_deserializer=context__pb2.Service.FromString, + response_serializer=context__pb2.ServiceId.SerializeToString, + ), + 'UpdateService': grpc.unary_unary_rpc_method_handler( + servicer.UpdateService, + request_deserializer=context__pb2.Service.FromString, + response_serializer=context__pb2.ServiceId.SerializeToString, + ), + 'DeleteService': grpc.unary_unary_rpc_method_handler( + servicer.DeleteService, + request_deserializer=context__pb2.ServiceId.FromString, + response_serializer=context__pb2.Empty.SerializeToString, + ), + 'GetConnectionList': grpc.unary_unary_rpc_method_handler( + servicer.GetConnectionList, + request_deserializer=context__pb2.ServiceId.FromString, + response_serializer=context__pb2.ConnectionList.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'service.ServiceService', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class ServiceService(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def CreateService(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, '/service.ServiceService/CreateService', + context__pb2.Service.SerializeToString, + context__pb2.ServiceId.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def UpdateService(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, '/service.ServiceService/UpdateService', + context__pb2.Service.SerializeToString, + context__pb2.ServiceId.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def DeleteService(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, '/service.ServiceService/DeleteService', + context__pb2.ServiceId.SerializeToString, + context__pb2.Empty.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def GetConnectionList(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, '/service.ServiceService/GetConnectionList', + context__pb2.ServiceId.SerializeToString, + context__pb2.ConnectionList.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/interdomain/requirements.in b/src/interdomain/requirements.in new file mode 100644 index 000000000..eb922871f --- /dev/null +++ b/src/interdomain/requirements.in @@ -0,0 +1,18 @@ +anytree +apscheduler +fastcache +flask-restful +grpcio-health-checking +grpcio +Jinja2 +netconf-client #1.7.3 +prometheus-client +pytest +pytest-benchmark +python-json-logger +pytz +redis +requests +xmltodict +p4runtime==1.3.0 +coverage diff --git a/src/interdomain/service/InterdomainService.py b/src/interdomain/service/InterdomainService.py new file mode 100644 index 000000000..1ae1c6772 --- /dev/null +++ b/src/interdomain/service/InterdomainService.py @@ -0,0 +1,60 @@ +from concurrent import futures + +import grpc + +from interdomain.service.InterdomainServiceServicerImpl import InterdomainServiceServicerImpl +from interdomain.Config import GRPC_SLICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD +from interdomain.proto.interdomain_pb2_grpc import add_InterdomainServiceServicer_to_server + +from grpc_health.v1 import health +from grpc_health.v1 import health_pb2 +from grpc_health.v1.health_pb2_grpc import add_HealthServicer_to_server + +from common.logger import getJSONLogger +LOGGER = getJSONLogger('interdomainservice-server') +LOGGER.setLevel('DEBUG') + +BIND_ADDRESS = '0.0.0.0' + +class InterdomainService: + def __init__(self, address=BIND_ADDRESS, slice_client=None, port=GRPC_INTERDOMAIN_PORT, max_workers=GRPC_MAX_WORKERS, + grace_period=GRPC_GRACE_PERIOD): + self.address = address + self.slice_client = slice_client + self.port = port + self.endpoint = None + self.max_workers = max_workers + self.grace_period = grace_period + self.monitoring_servicer = None + self.health_servicer = None + self.pool = None + self.server = None + + def start(self): + # create gRPC server + self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=self.max_workers)) # ,interceptors=(tracer_interceptor,)) + + # add monitoring servicer class to gRPC server + self.interdomain_servicer = InterdomainServiceServicerImpl() + add_InterdomainServiceServicer_to_server(self.interdomain_servicer, self.server) + + # add gRPC health checker servicer class to gRPC server + self.health_servicer = health.HealthServicer( + experimental_non_blocking=True, experimental_thread_pool=futures.ThreadPoolExecutor(max_workers=1)) + add_HealthServicer_to_server(self.health_servicer, self.server) + + # start server + endpoint = '{}:{}'.format(self.address, self.port) + LOGGER.info('Listening on {}'.format(endpoint)) + self.server.add_insecure_port(endpoint) + self.server.start() + self.health_servicer.set('', health_pb2.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') + diff --git a/src/interdomain/service/InterdomainServiceServicerImpl.py b/src/interdomain/service/InterdomainServiceServicerImpl.py new file mode 100644 index 000000000..fcadaa0c9 --- /dev/null +++ b/src/interdomain/service/InterdomainServiceServicerImpl.py @@ -0,0 +1,163 @@ +import os,grpc + +from prometheus_client import Summary +from prometheus_client import Counter + +from monitoring.service import SqliteTools, InfluxTools +from monitoring.proto import monitoring_pb2 +from monitoring.proto import monitoring_pb2_grpc + +from common.rpc_method_wrapper.ServiceExceptions import ServiceException +from common.logger import getJSONLogger + +from context.proto import context_pb2 + + +from device.Config import GRPC_SERVICE_PORT +from device.client.DeviceClient import DeviceClient +from device.proto import device_pb2 + +LOGGER = getJSONLogger('monitoringservice-server') +LOGGER.setLevel('DEBUG') + +MONITORING_GETINSTANTKPI_REQUEST_TIME = Summary('monitoring_getinstantkpi_processing_seconds', 'Time spent processing monitoring instant kpi request') +MONITORING_INCLUDEKPI_COUNTER = Counter('monitoring_includekpi_counter', 'Monitoring include kpi request counter') + +INFLUXDB_HOSTNAME = os.environ.get("INFLUXDB_HOSTNAME") +INFLUXDB_USER = os.environ.get("INFLUXDB_USER") +INFLUXDB_PASSWORD = os.environ.get("INFLUXDB_PASSWORD") +INFLUXDB_DATABASE = os.environ.get("INFLUXDB_DATABASE") + + +class MonitoringServiceServicerImpl(monitoring_pb2_grpc.MonitoringServiceServicer): + def __init__(self): + LOGGER.info('Init monitoringService') + + # Init sqlite monitoring db + self.sql_db = SqliteTools.SQLite('monitoring.db') + + # Create influx_db client + self.influx_db = InfluxTools.Influx(INFLUXDB_HOSTNAME,"8086",INFLUXDB_USER,INFLUXDB_PASSWORD,INFLUXDB_DATABASE) + + # CreateKpi (CreateKpiRequest) returns (KpiId) {} + def CreateKpi(self, request : monitoring_pb2.KpiDescriptor, grpc_context : grpc.ServicerContext) -> monitoring_pb2.KpiId : + # CREATEKPI_COUNTER_STARTED.inc() + LOGGER.info('CreateKpi') + try: + # Here the code to create a sqlite query to crete a KPI and return a KpiID + kpi_id = monitoring_pb2.KpiId() + + kpi_description = request.kpi_description + kpi_sample_type = request.kpi_sample_type + kpi_device_id = request.device_id.device_uuid.uuid + kpi_endpoint_id = request.endpoint_id.endpoint_uuid.uuid + kpi_service_id = request.service_id.service_uuid.uuid + + data = self.sql_db.insert_KPI(kpi_description, kpi_sample_type, kpi_device_id, kpi_endpoint_id, kpi_service_id) + + kpi_id.kpi_id.uuid = str(data) + + # CREATEKPI_COUNTER_COMPLETED.inc() + return kpi_id + except ServiceException as e: + LOGGER.exception('CreateKpi exception') + # CREATEKPI_COUNTER_FAILED.inc() + grpc_context.abort(e.code, e.details) + except Exception as e: # pragma: no cover + LOGGER.exception('CreateKpi exception') + # CREATEKPI_COUNTER_FAILED.inc() + grpc_context.abort(grpc.StatusCode.INTERNAL, str(e)) + + # rpc MonitorKpi (MonitorKpiRequest) returns (context.Empty) {} + def MonitorKpi ( self, request : monitoring_pb2.MonitorKpiRequest, grpc_context : grpc.ServicerContext) -> context_pb2.Empty: + + LOGGER.info('MonitorKpi') + try: + # Creates the request to send to the device service + monitor_device_request = device_pb2.MonitoringSettings() + + kpiDescriptor = self.GetKpiDescriptor(request.kpi_id, grpc_context) + + monitor_device_request.kpi_descriptor.CopyFrom(kpiDescriptor) + monitor_device_request.kpi_id.kpi_id.uuid = request.kpi_id.kpi_id.uuid + monitor_device_request.sampling_duration_s = request.sampling_duration_s + monitor_device_request.sampling_interval_s = request.sampling_interval_s + + deviceClient = DeviceClient(address="localhost", port=GRPC_SERVICE_PORT ) # instantiate the client + # deviceClient.MonitorDeviceKpi(monitor_device_request) + + return context_pb2.Empty() + except ServiceException as e: + LOGGER.exception('MonitorKpi exception') + # CREATEKPI_COUNTER_FAILED.inc() + grpc_context.abort(e.code, e.details) + except Exception as e: # pragma: no cover + LOGGER.exception('MonitorKpi exception') + # CREATEKPI_COUNTER_FAILED.inc() + + + # rpc IncludeKpi(IncludeKpiRequest) returns(context.Empty) {} + def IncludeKpi(self, request : monitoring_pb2.Kpi, grpc_context : grpc.ServicerContext) -> context_pb2.Empty: + + LOGGER.info('IncludeKpi') + + try: + kpiDescriptor = self.GetKpiDescriptor(request.kpi_id, grpc_context) + + kpiSampleType = kpiDescriptor.kpi_sample_type + kpiId = request.kpi_id.kpi_id.uuid + deviceId = kpiDescriptor.device_id.device_uuid.uuid + endpointId = kpiDescriptor.endpoint_id.endpoint_uuid.uuid + serviceId = kpiDescriptor.service_id.service_uuid.uuid + time_stamp = request.timestamp + kpi_value = request.kpi_value.intVal + + # Build the structure to be included as point in the influxDB + self.influx_db.write_KPI(time_stamp,kpiId,kpiSampleType,deviceId,endpointId,serviceId,kpi_value) + + self.influx_db.read_KPI_points() + + return context_pb2.Empty() + except ServiceException as e: + LOGGER.exception('IncludeKpi exception') + # CREATEKPI_COUNTER_FAILED.inc() + grpc_context.abort(e.code, e.details) + except Exception as e: # pragma: no cover + LOGGER.exception('IncludeKpi exception') + # CREATEKPI_COUNTER_FAILED.inc() + + def GetStreamKpi ( self, request, grpc_context : grpc.ServicerContext): + # receives monitoring.KpiId returns stream monitoring.Kpi + LOGGER.info('GetStreamKpi') + yield monitoring_pb2.Kpi() + + @MONITORING_GETINSTANTKPI_REQUEST_TIME.time() + def GetInstantKpi ( self, request, grpc_context : grpc.ServicerContext): + # receives monitoring.KpiId returns monitoring.Kpi + LOGGER.info('GetInstantKpi') + return monitoring_pb2.Kpi() + + + def GetKpiDescriptor(self, request : monitoring_pb2.KpiId, grpc_context : grpc.ServicerContext) -> monitoring_pb2.KpiDescriptor: + LOGGER.info('getting Kpi by KpiID') + try: + kpi_db = self.sql_db.get_KPI(int(request.kpi_id.uuid)) + print(self.sql_db.get_KPIS()) + + kpiDescriptor = monitoring_pb2.KpiDescriptor() + + kpiDescriptor.kpi_description = kpi_db[1] + kpiDescriptor.kpi_sample_type = kpi_db[2] + kpiDescriptor.device_id.device_uuid.uuid = str(kpi_db[3]) + kpiDescriptor.endpoint_id.endpoint_uuid.uuid = str(kpi_db[4]) + kpiDescriptor.service_id.service_uuid.uuid = str(kpi_db[5]) + + return kpiDescriptor + except ServiceException as e: + LOGGER.exception('GetKpiDescriptor exception') + grpc_context.abort(e.code, e.details) + + except Exception as e: # pragma: no cover + LOGGER.exception('GetKpiDescriptor exception') + + diff --git a/src/interdomain/service/__init__.py b/src/interdomain/service/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/interdomain/service/__main__.py b/src/interdomain/service/__main__.py new file mode 100644 index 000000000..486451682 --- /dev/null +++ b/src/interdomain/service/__main__.py @@ -0,0 +1,62 @@ +import logging, signal, sys, threading +from prometheus_client import start_http_server +from common.Settings import get_setting +from slice.client.SliceClient import SliceClient + +from interdomain.Config import ( + SLICE_SERVICE_HOST, SLICE_SERVICE_PORT, GRPC_INTERDOMAIN_PORT, + GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, METRICS_PORT) +from .InterdomainService import InterdomainService + + +terminate = threading.Event() +LOGGER = None + +def signal_handler(signal, frame): # pylint: disable=redefined-outer-name + LOGGER.warning('Terminate signal received') + terminate.set() + +def main(): + global LOGGER # pylint: disable=global-statement + + grpc_service_port = get_setting('INTERDOMAINSERVICE_SERVICE_PORT_GRPC', default=GRPC_INTERDOMAIN_PORT ) + max_workers = get_setting('MAX_WORKERS', default=GRPC_MAX_WORKERS ) + grace_period = get_setting('GRACE_PERIOD', default=GRPC_GRACE_PERIOD ) + log_level = get_setting('LOG_LEVEL', default=LOG_LEVEL ) + metrics_port = get_setting('METRICS_PORT', default=METRICS_PORT ) + slice_service_host = get_setting('SLICESERVICE_SERVICE_HOST', default=SLICE_SERVICE_HOST) + slice_service_port = get_setting('SLICESERVICE_SERVICE_PORT_GRPC', default=SLICE_SERVICE_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) + + # Initialize Slice Client + if slice_service_host is None or slice_service_port is None: + raise Exception('Wrong address({:s}):port({:s}) of Device component'.format( + str(slice_service_host), str(slice_service_port))) + slice_client = SliceClient(slice_service_host, slice_service_port) + + # Starting service service + grpc_interdomain = InterdomainService( slice_client=slice_client, port=grpc_interdomain_port, max_workers=max_workers, grace_period=grace_period) + grpc_interdomain.start() + + # Wait for Ctrl+C or termination signal + while not terminate.wait(timeout=0.1): pass + + LOGGER.info('Terminating...') + grpc_interdomain.stop() + + LOGGER.info('Bye') + return 0 + +if __name__ == '__main__': + sys.exit(main()) -- GitLab From aa7e998cc1a3d25a8007672b4d5d918560163f81 Mon Sep 17 00:00:00 2001 From: Ricard Vilalta Date: Tue, 8 Mar 2022 05:44:39 -0500 Subject: [PATCH 002/353] First unitary tests --- .../service/InterdomainServiceServicerImpl.py | 165 ++++-------------- src/interdomain/tests/__init__.py | 0 src/interdomain/tests/test_unitary.py | 131 ++++++++++++++ 3 files changed, 165 insertions(+), 131 deletions(-) create mode 100644 src/interdomain/tests/__init__.py create mode 100644 src/interdomain/tests/test_unitary.py diff --git a/src/interdomain/service/InterdomainServiceServicerImpl.py b/src/interdomain/service/InterdomainServiceServicerImpl.py index fcadaa0c9..76357d876 100644 --- a/src/interdomain/service/InterdomainServiceServicerImpl.py +++ b/src/interdomain/service/InterdomainServiceServicerImpl.py @@ -1,163 +1,66 @@ import os,grpc -from prometheus_client import Summary -from prometheus_client import Counter - -from monitoring.service import SqliteTools, InfluxTools -from monitoring.proto import monitoring_pb2 -from monitoring.proto import monitoring_pb2_grpc +from interdomain.proto import interdomain_pb2 +from interdomain.proto import interdomain_pb2_grpc from common.rpc_method_wrapper.ServiceExceptions import ServiceException from common.logger import getJSONLogger from context.proto import context_pb2 +from slice.Config import GRPC_SERVICE_PORT +from slice.client.SliceClient import SliceClient +from slice.proto import slice_pb2 -from device.Config import GRPC_SERVICE_PORT -from device.client.DeviceClient import DeviceClient -from device.proto import device_pb2 - -LOGGER = getJSONLogger('monitoringservice-server') +LOGGER = getJSONLogger('interdomainservice-server') LOGGER.setLevel('DEBUG') -MONITORING_GETINSTANTKPI_REQUEST_TIME = Summary('monitoring_getinstantkpi_processing_seconds', 'Time spent processing monitoring instant kpi request') -MONITORING_INCLUDEKPI_COUNTER = Counter('monitoring_includekpi_counter', 'Monitoring include kpi request counter') - -INFLUXDB_HOSTNAME = os.environ.get("INFLUXDB_HOSTNAME") -INFLUXDB_USER = os.environ.get("INFLUXDB_USER") -INFLUXDB_PASSWORD = os.environ.get("INFLUXDB_PASSWORD") -INFLUXDB_DATABASE = os.environ.get("INFLUXDB_DATABASE") - - -class MonitoringServiceServicerImpl(monitoring_pb2_grpc.MonitoringServiceServicer): +class InterdomainServiceServicerImpl(interdomain_pb2_grpc.InterdomainServiceServicer): def __init__(self): - LOGGER.info('Init monitoringService') - - # Init sqlite monitoring db - self.sql_db = SqliteTools.SQLite('monitoring.db') - - # Create influx_db client - self.influx_db = InfluxTools.Influx(INFLUXDB_HOSTNAME,"8086",INFLUXDB_USER,INFLUXDB_PASSWORD,INFLUXDB_DATABASE) - - # CreateKpi (CreateKpiRequest) returns (KpiId) {} - def CreateKpi(self, request : monitoring_pb2.KpiDescriptor, grpc_context : grpc.ServicerContext) -> monitoring_pb2.KpiId : - # CREATEKPI_COUNTER_STARTED.inc() - LOGGER.info('CreateKpi') - try: - # Here the code to create a sqlite query to crete a KPI and return a KpiID - kpi_id = monitoring_pb2.KpiId() - - kpi_description = request.kpi_description - kpi_sample_type = request.kpi_sample_type - kpi_device_id = request.device_id.device_uuid.uuid - kpi_endpoint_id = request.endpoint_id.endpoint_uuid.uuid - kpi_service_id = request.service_id.service_uuid.uuid + LOGGER.info('Init InterdomainService') - data = self.sql_db.insert_KPI(kpi_description, kpi_sample_type, kpi_device_id, kpi_endpoint_id, kpi_service_id) + # rpc Authenticate (context.TeraFlowController) returns (context.AuthenticationResult) {} + def Authenticate(self, request : context_pb2.TeraFlowController) -> context_pb2.AuthenticationResult : + LOGGER.info('Authenticate') + auth_result = context_pb2.AuthenticationResult() + auth_result.context_id = 0 + auth_result.authenticated = True + return auth_result - kpi_id.kpi_id.uuid = str(data) + # rpc LookUpSlice(slice.TransportSlice) returns (slice.SliceId) {} + def LookUpSlice ( self, request : slice_pb2.TransportSlice) -> slice_pb2.SliceId: - # CREATEKPI_COUNTER_COMPLETED.inc() - return kpi_id - except ServiceException as e: - LOGGER.exception('CreateKpi exception') - # CREATEKPI_COUNTER_FAILED.inc() - grpc_context.abort(e.code, e.details) - except Exception as e: # pragma: no cover - LOGGER.exception('CreateKpi exception') - # CREATEKPI_COUNTER_FAILED.inc() - grpc_context.abort(grpc.StatusCode.INTERNAL, str(e)) - - # rpc MonitorKpi (MonitorKpiRequest) returns (context.Empty) {} - def MonitorKpi ( self, request : monitoring_pb2.MonitorKpiRequest, grpc_context : grpc.ServicerContext) -> context_pb2.Empty: - - LOGGER.info('MonitorKpi') + LOGGER.info('LookUpSlice') try: - # Creates the request to send to the device service - monitor_device_request = device_pb2.MonitoringSettings() - - kpiDescriptor = self.GetKpiDescriptor(request.kpi_id, grpc_context) - - monitor_device_request.kpi_descriptor.CopyFrom(kpiDescriptor) - monitor_device_request.kpi_id.kpi_id.uuid = request.kpi_id.kpi_id.uuid - monitor_device_request.sampling_duration_s = request.sampling_duration_s - monitor_device_request.sampling_interval_s = request.sampling_interval_s + slice_id = slice_pb2.SliceId() - deviceClient = DeviceClient(address="localhost", port=GRPC_SERVICE_PORT ) # instantiate the client - # deviceClient.MonitorDeviceKpi(monitor_device_request) + return sliceId + except Exception as e: + LOGGER.exception('LookUpSlice exception') - return context_pb2.Empty() - except ServiceException as e: - LOGGER.exception('MonitorKpi exception') - # CREATEKPI_COUNTER_FAILED.inc() - grpc_context.abort(e.code, e.details) - except Exception as e: # pragma: no cover - LOGGER.exception('MonitorKpi exception') - # CREATEKPI_COUNTER_FAILED.inc() - # rpc IncludeKpi(IncludeKpiRequest) returns(context.Empty) {} - def IncludeKpi(self, request : monitoring_pb2.Kpi, grpc_context : grpc.ServicerContext) -> context_pb2.Empty: + # rpc OrderSliceFromCatalog(slice.TransportSlice) returns (slice.SliceStatus) {} + def OrderSliceFromCatalog(self, request : slice_pb2.TransportSlice) -> slice_pb2.SliceStatus: - LOGGER.info('IncludeKpi') + LOGGER.info('OrderSliceFromCatalog') try: - kpiDescriptor = self.GetKpiDescriptor(request.kpi_id, grpc_context) - - kpiSampleType = kpiDescriptor.kpi_sample_type - kpiId = request.kpi_id.kpi_id.uuid - deviceId = kpiDescriptor.device_id.device_uuid.uuid - endpointId = kpiDescriptor.endpoint_id.endpoint_uuid.uuid - serviceId = kpiDescriptor.service_id.service_uuid.uuid - time_stamp = request.timestamp - kpi_value = request.kpi_value.intVal - - # Build the structure to be included as point in the influxDB - self.influx_db.write_KPI(time_stamp,kpiId,kpiSampleType,deviceId,endpointId,serviceId,kpi_value) - - self.influx_db.read_KPI_points() - - return context_pb2.Empty() - except ServiceException as e: - LOGGER.exception('IncludeKpi exception') - # CREATEKPI_COUNTER_FAILED.inc() - grpc_context.abort(e.code, e.details) + slice_status=slice_pb2.SliceStatus() + return slice_status except Exception as e: # pragma: no cover - LOGGER.exception('IncludeKpi exception') - # CREATEKPI_COUNTER_FAILED.inc() + LOGGER.exception('OrderSliceFromCatalog exception') - def GetStreamKpi ( self, request, grpc_context : grpc.ServicerContext): - # receives monitoring.KpiId returns stream monitoring.Kpi - LOGGER.info('GetStreamKpi') - yield monitoring_pb2.Kpi() - @MONITORING_GETINSTANTKPI_REQUEST_TIME.time() - def GetInstantKpi ( self, request, grpc_context : grpc.ServicerContext): - # receives monitoring.KpiId returns monitoring.Kpi - LOGGER.info('GetInstantKpi') - return monitoring_pb2.Kpi() + # rpc CreateSliceAndAddToCatalog(slice.TransportSlice) returns (slice.SliceStatus) {} + def CreateSliceAndAddToCatalog(self, request : slice_pb2.TransportSlice) -> slice_pb2.SliceStatus: + LOGGER.info('OrderSliceFromCatalog') - def GetKpiDescriptor(self, request : monitoring_pb2.KpiId, grpc_context : grpc.ServicerContext) -> monitoring_pb2.KpiDescriptor: - LOGGER.info('getting Kpi by KpiID') try: - kpi_db = self.sql_db.get_KPI(int(request.kpi_id.uuid)) - print(self.sql_db.get_KPIS()) - - kpiDescriptor = monitoring_pb2.KpiDescriptor() - - kpiDescriptor.kpi_description = kpi_db[1] - kpiDescriptor.kpi_sample_type = kpi_db[2] - kpiDescriptor.device_id.device_uuid.uuid = str(kpi_db[3]) - kpiDescriptor.endpoint_id.endpoint_uuid.uuid = str(kpi_db[4]) - kpiDescriptor.service_id.service_uuid.uuid = str(kpi_db[5]) - - return kpiDescriptor - except ServiceException as e: - LOGGER.exception('GetKpiDescriptor exception') - grpc_context.abort(e.code, e.details) - + slice_status=slice_pb2.SliceStatus() + return slice_status except Exception as e: # pragma: no cover - LOGGER.exception('GetKpiDescriptor exception') + LOGGER.exception('OrderSliceFromCatalog exception') diff --git a/src/interdomain/tests/__init__.py b/src/interdomain/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/interdomain/tests/test_unitary.py b/src/interdomain/tests/test_unitary.py new file mode 100644 index 000000000..57fe4d891 --- /dev/null +++ b/src/interdomain/tests/test_unitary.py @@ -0,0 +1,131 @@ +import logging, grpc +import os +import sqlite3 + +import pytest +from typing import Tuple + +from interdomain.proto import context_pb2, kpi_sample_types_pb2, monitoring_pb2 +from interdomain.client.interdomain_client import InterdomainClient +from interdomain.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD +from interdomain.service.InterdomainService import InterdomainService + +from common.orm.Database import Database +from common.orm.Factory import get_database_backend, BackendEnum as DatabaseBackendEnum +from common.message_broker.Factory import get_messagebroker_backend, BackendEnum as MessageBrokerBackendEnum +from common.message_broker.MessageBroker import MessageBroker + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +########################### +# Tests Setup +########################### + +SERVER_ADDRESS = '127.0.0.1' +LISTEN_ADDRESS = '[::]' +GRPC_PORT_MONITORING = 9090 + +GRPC_PORT_CONTEXT = 10000 + grpc_port_context # avoid privileged ports + +SCENARIOS = [ # comment/uncomment scenarios to activate/deactivate them in the test unit + ('all_inmemory', DatabaseBackendEnum.INMEMORY, {}, MessageBrokerBackendEnum.INMEMORY, {} ), +] + + +# This fixture will be requested by test cases and last during testing session +@pytest.fixture(scope='session') +def interdomain_service(): + LOGGER.warning('interdomain_service begin') + + interdomain_port = GRPC_INTERDOMAIN_PORT + max_workers = GRPC_MAX_WORKERS + grace_period = GRPC_GRACE_PERIOD + + LOGGER.info('Initializing InterdomainService...') + grpc_service = InterdomainService(port=interdomain_port, max_workers=max_workers, grace_period=grace_period) + server = grpc_service.start() + + # yield the server, when test finishes, execution will resume to stop it + LOGGER.warning('interdomain_service yielding') + yield server + + LOGGER.info('Terminating InterdomainService...') + grpc_service.stop() + +# This fixture will be requested by test cases and last during testing session. +# The client requires the server, so client fixture has the server as dependency. +@pytest.fixture(scope='session') +def interdomain_client(interdomain_service): + LOGGER.warning('interdomain_client begin') + client = InterdomainClient(server=SERVER_ADDRESS, port=GRPC_PORT_INTERDOMAIN) # instantiate the client + LOGGER.warning('interdomain_client returning') + return client + +# This fixture will be requested by test cases and last during testing session. +@pytest.fixture(scope='session') +def create_TeraFlowController(): + LOGGER.warning('create_TeraFlowController begin') + # form request + tf_ctl = context_pb2.TeraFlowController() + tf_ctl.context_id = context_pb2.ContextId() + tf_ctl.context_id.context_uuid = context_pb2.Uuid() + tf_ctl.context_id.context_uuid.uuid = str(1) + tf_ctl.ip_address = "127.0.0.1" + tf_ctl.port = 9090 + return tf_ctl + +@pytest.fixture(scope='session') +def create_TransportSlice(): + LOGGER.warning('create_TransportSlice begin') + + # form request + slice_req = slice_pb2.TransportSlice() + slice_req.contextId = context_pb2.ContextId() + slice_req.contextId.context_uuid = context_pb2.Uuid() + slice_req.contextId.context_uuid.uuid = str(1) + slice_req.slice_id = context_pb2.Uuid() + slice_req.slice_id.context_uuid.uuid = str(1) + + return slice_req + + +########################### +# Tests Implementation +########################### + + +# Test case that makes use of client fixture to test server's CreateKpi method +def test_Authenticate(interdomain_client,create_TeraFlowController): + # make call to server + LOGGER.warning('test_Authenticate requesting') + response = interdomain_client.Authenticate(create_TeraFlowController) + LOGGER.debug(str(response)) + assert isinstance(response, context.AuthenticationResult) + +# Test case that makes use of client fixture to test server's MonitorKpi method +def test_LookUpSlice(interdomain_client,create_TransportSlice): + LOGGER.warning('test_LookUpSlice begin') + + response = interdomain_client.LookUpSlice(create_TransportSlice) + LOGGER.debug(str(response)) + assert isinstance(response, slice.SliceId) + +# Test case that makes use of client fixture to test server's GetStreamKpi method +def test_CreateSliceAndAddToCatalog(interdomain_client,create_TransportSlice): + LOGGER.warning('test_CreateSliceAndAddToCatalog begin') + response = interdomain_client.CreateSliceAndAddToCatalog(create_TransportSlice) + LOGGER.debug(str(response)) + assert isinstance(response, slice.SliceId) + +# Test case that makes use of client fixture to test server's IncludeKpi method +def test_OrderSliceFromCatalog(interdomain_client,create_TransportSlice): + # make call to server + LOGGER.warning('test_OrderSliceFromCatalog requesting') + response = interdomain_client.OrderSliceFromCatalog(create_TransportSlice) + LOGGER.debug(str(response)) + assert isinstance(response, slice.SliceId) + + + + -- GitLab From 078b197655adeb738a8f6f73e1319bcb7b3bd557 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Wed, 9 Mar 2022 18:39:56 +0100 Subject: [PATCH 003/353] Created skeleton files for Interdomain component --- manifests/interdomainservice.yaml | 64 +++++++++++++++++++++++++++++ src/interdomain/Config.py | 18 +++++++- src/interdomain/Dockerfile | 20 +++++++-- src/interdomain/client/__init__.py | 14 +++++++ src/interdomain/proto/__init__.py | 14 +++++++ src/interdomain/service/__main__.py | 45 ++++++++++++++------ src/interdomain/tests/__init__.py | 14 +++++++ 7 files changed, 172 insertions(+), 17 deletions(-) create mode 100644 manifests/interdomainservice.yaml diff --git a/manifests/interdomainservice.yaml b/manifests/interdomainservice.yaml new file mode 100644 index 000000000..6a107eec2 --- /dev/null +++ b/manifests/interdomainservice.yaml @@ -0,0 +1,64 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: interdomainservice +spec: + selector: + matchLabels: + app: interdomainservice + template: + metadata: + labels: + app: interdomainservice + spec: + terminationGracePeriodSeconds: 5 + containers: + - name: server + image: registry.gitlab.com/teraflow-h2020/controller/interdomain:latest + imagePullPolicy: Always + ports: + - containerPort: 10010 + env: + - name: LOG_LEVEL + value: "INFO" + readinessProbe: + exec: + command: ["/bin/grpc_health_probe", "-addr=:10010"] + livenessProbe: + exec: + command: ["/bin/grpc_health_probe", "-addr=:10010"] + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: 700m + memory: 1024Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: interdomainservice +spec: + type: ClusterIP + selector: + app: interdomainservice + ports: + - name: grpc + protocol: TCP + port: 10010 + targetPort: 10010 diff --git a/src/interdomain/Config.py b/src/interdomain/Config.py index b2e558ac1..2e236fa9f 100644 --- a/src/interdomain/Config.py +++ b/src/interdomain/Config.py @@ -1,10 +1,24 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging # General settings LOG_LEVEL = logging.WARNING # gRPC settings -GRPC_INTERDOMAIN_PORT = 9090 +GRPC_SERVICE_PORT = 10010 GRPC_MAX_WORKERS = 10 GRPC_GRACE_PERIOD = 60 @@ -13,6 +27,6 @@ METRICS_PORT = 9192 # Dependency micro-service connection settings SLICE_SERVICE_HOST = '127.0.0.1' -SLICE_SERVICE_PORT = 1010 +SLICE_SERVICE_PORT = 4040 diff --git a/src/interdomain/Dockerfile b/src/interdomain/Dockerfile index 26438173c..7ae62928e 100644 --- a/src/interdomain/Dockerfile +++ b/src/interdomain/Dockerfile @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 python:3-slim # Install dependencies @@ -25,7 +39,7 @@ RUN mkdir -p /var/teraflow/interdomain # Get Python packages per module COPY interdomain/requirements.in interdomain/requirements.in RUN pip-compile --output-file=interdomain/requirements.txt interdomain/requirements.in -RUN python3 -m pip install -r interdomain/requirements.in +RUN python3 -m pip install -r interdomain/requirements.txt # Add files into working directory COPY common/. common @@ -35,5 +49,5 @@ COPY monitoring/. monitoring COPY service/. service COPY interdomain/. interdomain -# Start service interdomain -ENTRYPOINT ["python", "-m", "service.interdomain"] +# Start interdomain service +ENTRYPOINT ["python", "-m", "interdomain.service"] diff --git a/src/interdomain/client/__init__.py b/src/interdomain/client/__init__.py index e69de29bb..70a332512 100644 --- a/src/interdomain/client/__init__.py +++ b/src/interdomain/client/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/interdomain/proto/__init__.py b/src/interdomain/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/interdomain/proto/__init__.py +++ b/src/interdomain/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/interdomain/service/__main__.py b/src/interdomain/service/__main__.py index 486451682..8fbe01770 100644 --- a/src/interdomain/service/__main__.py +++ b/src/interdomain/service/__main__.py @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, signal, sys, threading from prometheus_client import start_http_server from common.Settings import get_setting @@ -10,7 +24,7 @@ from .InterdomainService import InterdomainService terminate = threading.Event() -LOGGER = None +LOGGER : logging.Logger = None def signal_handler(signal, frame): # pylint: disable=redefined-outer-name LOGGER.warning('Terminate signal received') @@ -19,18 +33,23 @@ def signal_handler(signal, frame): # pylint: disable=redefined-outer-name def main(): global LOGGER # pylint: disable=global-statement - grpc_service_port = get_setting('INTERDOMAINSERVICE_SERVICE_PORT_GRPC', default=GRPC_INTERDOMAIN_PORT ) - max_workers = get_setting('MAX_WORKERS', default=GRPC_MAX_WORKERS ) - grace_period = get_setting('GRACE_PERIOD', default=GRPC_GRACE_PERIOD ) - log_level = get_setting('LOG_LEVEL', default=LOG_LEVEL ) - metrics_port = get_setting('METRICS_PORT', default=METRICS_PORT ) - slice_service_host = get_setting('SLICESERVICE_SERVICE_HOST', default=SLICE_SERVICE_HOST) - slice_service_port = get_setting('SLICESERVICE_SERVICE_PORT_GRPC', default=SLICE_SERVICE_PORT) - + grpc_service_port = get_setting('INTERDOMAINSERVICE_SERVICE_PORT_GRPC', default=GRPC_SERVICE_PORT ) + max_workers = get_setting('MAX_WORKERS', default=GRPC_MAX_WORKERS ) + grace_period = get_setting('GRACE_PERIOD', default=GRPC_GRACE_PERIOD ) + log_level = get_setting('LOG_LEVEL', default=LOG_LEVEL ) + metrics_port = get_setting('METRICS_PORT', default=METRICS_PORT ) logging.basicConfig(level=log_level) LOGGER = logging.getLogger(__name__) + wait_for_environment_variables([ + 'CONTEXTSERVICE_SERVICE_HOST', 'CONTEXTSERVICE_SERVICE_PORT_GRPC', + 'MONITORINGSERVICE_SERVICE_HOST', 'MONITORINGSERVICE_SERVICE_PORT_GRPC' + ]) + + slice_service_host = get_setting('SLICESERVICE_SERVICE_HOST', default=SLICE_SERVICE_HOST ) + slice_service_port = get_setting('SLICESERVICE_SERVICE_PORT_GRPC', default=SLICE_SERVICE_PORT ) + signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) @@ -41,12 +60,14 @@ def main(): # Initialize Slice Client if slice_service_host is None or slice_service_port is None: - raise Exception('Wrong address({:s}):port({:s}) of Device component'.format( + raise Exception('Wrong address({:s}):port({:s}) of Slice component'.format( str(slice_service_host), str(slice_service_port))) slice_client = SliceClient(slice_service_host, slice_service_port) - # Starting service service - grpc_interdomain = InterdomainService( slice_client=slice_client, port=grpc_interdomain_port, max_workers=max_workers, grace_period=grace_period) + # Starting Interdomain service + grpc_interdomain = InterdomainService( + slice_client=slice_client, port=grpc_interdomain_port, max_workers=max_workers, + grace_period=grace_period) grpc_interdomain.start() # Wait for Ctrl+C or termination signal diff --git a/src/interdomain/tests/__init__.py b/src/interdomain/tests/__init__.py index e69de29bb..70a332512 100644 --- a/src/interdomain/tests/__init__.py +++ b/src/interdomain/tests/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + -- GitLab From ba6a772a1deb42ebf32e2b93a35018695f15ff04 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Wed, 9 Mar 2022 18:40:45 +0100 Subject: [PATCH 004/353] Created skeleton for OECC/PSC'22 functional test --- oeccpsc22 | 1 + src/tests/.gitlab-ci.yml | 3 +- src/tests/oeccpsc22/README.md | 8 + src/tests/oeccpsc22/__init__.py | 14 ++ src/tests/oeccpsc22/deploy_in_kubernetes.sh | 33 +++ .../oeccpsc22/expose_services_teraflow_1.yaml | 101 +++++++++ .../oeccpsc22/expose_services_teraflow_2.yaml | 101 +++++++++ src/tests/oeccpsc22/run_test_01_bootstrap.sh | 56 +++++ .../oeccpsc22/run_test_02_create_service.sh | 41 ++++ .../oeccpsc22/run_test_03_delete_service.sh | 41 ++++ src/tests/oeccpsc22/run_test_04_cleanup.sh | 41 ++++ src/tests/oeccpsc22/show_deploy.sh | 26 +++ src/tests/oeccpsc22/tests/.gitignore | 2 + src/tests/oeccpsc22/tests/Objects_Domain_1.py | 121 ++++++++++ src/tests/oeccpsc22/tests/Objects_Domain_2.py | 121 ++++++++++ src/tests/oeccpsc22/tests/Objects_Service.py | 35 +++ src/tests/oeccpsc22/tests/Tools.py | 25 +++ src/tests/oeccpsc22/tests/__init__.py | 14 ++ .../tests/test_functional_bootstrap.py | 208 ++++++++++++++++++ .../tests/test_functional_cleanup.py | 123 +++++++++++ .../tests/test_functional_create_service.py | 129 +++++++++++ .../tests/test_functional_delete_service.py | 134 +++++++++++ 22 files changed, 1377 insertions(+), 1 deletion(-) create mode 120000 oeccpsc22 create mode 100644 src/tests/oeccpsc22/README.md create mode 100644 src/tests/oeccpsc22/__init__.py create mode 100755 src/tests/oeccpsc22/deploy_in_kubernetes.sh create mode 100644 src/tests/oeccpsc22/expose_services_teraflow_1.yaml create mode 100644 src/tests/oeccpsc22/expose_services_teraflow_2.yaml create mode 100755 src/tests/oeccpsc22/run_test_01_bootstrap.sh create mode 100755 src/tests/oeccpsc22/run_test_02_create_service.sh create mode 100755 src/tests/oeccpsc22/run_test_03_delete_service.sh create mode 100755 src/tests/oeccpsc22/run_test_04_cleanup.sh create mode 100755 src/tests/oeccpsc22/show_deploy.sh create mode 100644 src/tests/oeccpsc22/tests/.gitignore create mode 100644 src/tests/oeccpsc22/tests/Objects_Domain_1.py create mode 100644 src/tests/oeccpsc22/tests/Objects_Domain_2.py create mode 100644 src/tests/oeccpsc22/tests/Objects_Service.py create mode 100644 src/tests/oeccpsc22/tests/Tools.py create mode 100644 src/tests/oeccpsc22/tests/__init__.py create mode 100644 src/tests/oeccpsc22/tests/test_functional_bootstrap.py create mode 100644 src/tests/oeccpsc22/tests/test_functional_cleanup.py create mode 100644 src/tests/oeccpsc22/tests/test_functional_create_service.py create mode 100644 src/tests/oeccpsc22/tests/test_functional_delete_service.py diff --git a/oeccpsc22 b/oeccpsc22 new file mode 120000 index 000000000..4f55befad --- /dev/null +++ b/oeccpsc22 @@ -0,0 +1 @@ +src/tests/oeccpsc22/ \ No newline at end of file diff --git a/src/tests/.gitlab-ci.yml b/src/tests/.gitlab-ci.yml index e663b09ec..6fcac6480 100644 --- a/src/tests/.gitlab-ci.yml +++ b/src/tests/.gitlab-ci.yml @@ -14,4 +14,5 @@ # include the individual .gitlab-ci.yml of each integration test include: - - local: '/src/tests/ofc22_bootstrap_monitor_l3vpn/.gitlab-ci.yml' + - local: '/src/tests/ofc22/.gitlab-ci.yml' + - local: '/src/tests/oeccpsc22/.gitlab-ci.yml' diff --git a/src/tests/oeccpsc22/README.md b/src/tests/oeccpsc22/README.md new file mode 100644 index 000000000..42e0228a5 --- /dev/null +++ b/src/tests/oeccpsc22/README.md @@ -0,0 +1,8 @@ +# OECC/PSC'22 Paper - Interdomain slices +This functional test reproduces the experiment in paper "... paper title ..." presented at OECC/PSC'22 conference +[OECC/PSC'22](... demo link ...). + +## Functional test folder +This functional test can be found in folder `./src/tests/oeccpsc22/`. A convenience alias `./oeccpsc22/` pointing to that folder has been defined. + +# TO BE WRITTEN diff --git a/src/tests/oeccpsc22/__init__.py b/src/tests/oeccpsc22/__init__.py new file mode 100644 index 000000000..70a332512 --- /dev/null +++ b/src/tests/oeccpsc22/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/tests/oeccpsc22/deploy_in_kubernetes.sh b/src/tests/oeccpsc22/deploy_in_kubernetes.sh new file mode 100755 index 000000000..f40257f34 --- /dev/null +++ b/src/tests/oeccpsc22/deploy_in_kubernetes.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + + +# OECC/PSC 22 deployment settings + +export REGISTRY_IMAGE="" +export COMPONENTS="context device service compute monitoring interdomain webui" # slice +export IMAGE_TAG="oeccpsc22" +export K8S_HOSTNAME="kubernetes-master" +#export GRAFANA_PASSWORD="admin123+" + +# Deploy TeraFlow instance 1 +export K8S_NAMESPACE="oeccpsc22-1" +export EXTRA_MANIFESTS="./oeccpsc22/expose_services_teraflow_1.yaml" +./deploy_in_kubernetes.sh + +# Deploy TeraFlow instance 2 +export K8S_NAMESPACE="oeccpsc22-2" +export EXTRA_MANIFESTS="./oeccpsc22/expose_services_teraflow_2.yaml" +./deploy_in_kubernetes.sh diff --git a/src/tests/oeccpsc22/expose_services_teraflow_1.yaml b/src/tests/oeccpsc22/expose_services_teraflow_1.yaml new file mode 100644 index 000000000..6d2f0bed7 --- /dev/null +++ b/src/tests/oeccpsc22/expose_services_teraflow_1.yaml @@ -0,0 +1,101 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +apiVersion: v1 +kind: Service +metadata: + name: remote-teraflow +spec: + type: ExternalName + externalName: interdomainservice.oeccpsc22-2.svc.cluster.local + ports: + - name: grpc + protocol: TCP + port: 10010 +--- +apiVersion: v1 +kind: Service +metadata: + name: contextservice-public + labels: + app: contextservice +spec: + type: NodePort + selector: + app: contextservice + ports: + - name: grpc + protocol: TCP + port: 1010 + targetPort: 1010 + nodePort: 30111 + - name: redis + protocol: TCP + port: 6379 + targetPort: 6379 + nodePort: 30631 +--- +apiVersion: v1 +kind: Service +metadata: + name: deviceservice-public + labels: + app: deviceservice +spec: + type: NodePort + selector: + app: deviceservice + ports: + - name: grpc + protocol: TCP + port: 2020 + targetPort: 2020 + nodePort: 30221 +--- +apiVersion: v1 +kind: Service +metadata: + name: computeservice-public +spec: + type: NodePort + selector: + app: computeservice + ports: + - name: http + protocol: TCP + port: 8080 + targetPort: 8080 + nodePort: 30881 +--- +apiVersion: v1 +kind: Service +metadata: + name: webuiservice-public + labels: + app: webuiservice +spec: + type: NodePort + selector: + app: webuiservice + ports: + - name: http + protocol: TCP + port: 8004 + targetPort: 8004 + nodePort: 30801 + - name: grafana + protocol: TCP + port: 3000 + targetPort: 3000 + nodePort: 30301 diff --git a/src/tests/oeccpsc22/expose_services_teraflow_2.yaml b/src/tests/oeccpsc22/expose_services_teraflow_2.yaml new file mode 100644 index 000000000..32974848e --- /dev/null +++ b/src/tests/oeccpsc22/expose_services_teraflow_2.yaml @@ -0,0 +1,101 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +apiVersion: v1 +kind: Service +metadata: + name: remote-teraflow +spec: + type: ExternalName + externalName: interdomainservice.oeccpsc22-2.svc.cluster.local + ports: + - name: grpc + protocol: TCP + port: 10010 +--- +apiVersion: v1 +kind: Service +metadata: + name: contextservice-public + labels: + app: contextservice +spec: + type: NodePort + selector: + app: contextservice + ports: + - name: grpc + protocol: TCP + port: 1010 + targetPort: 1010 + nodePort: 30112 + - name: redis + protocol: TCP + port: 6379 + targetPort: 6379 + nodePort: 30632 +--- +apiVersion: v1 +kind: Service +metadata: + name: deviceservice-public + labels: + app: deviceservice +spec: + type: NodePort + selector: + app: deviceservice + ports: + - name: grpc + protocol: TCP + port: 2020 + targetPort: 2020 + nodePort: 30222 +--- +apiVersion: v1 +kind: Service +metadata: + name: computeservice-public +spec: + type: NodePort + selector: + app: computeservice + ports: + - name: http + protocol: TCP + port: 8080 + targetPort: 8080 + nodePort: 30882 +--- +apiVersion: v1 +kind: Service +metadata: + name: webuiservice-public + labels: + app: webuiservice +spec: + type: NodePort + selector: + app: webuiservice + ports: + - name: http + protocol: TCP + port: 8004 + targetPort: 8004 + nodePort: 30802 + - name: grafana + protocol: TCP + port: 3000 + targetPort: 3000 + nodePort: 30302 diff --git a/src/tests/oeccpsc22/run_test_01_bootstrap.sh b/src/tests/oeccpsc22/run_test_01_bootstrap.sh new file mode 100755 index 000000000..7b816984a --- /dev/null +++ b/src/tests/oeccpsc22/run_test_01_bootstrap.sh @@ -0,0 +1,56 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + + +PROJECTDIR=`pwd` + +cd $PROJECTDIR/src +RCFILE=$PROJECTDIR/coverage/.coveragerc +COVERAGEFILE=$PROJECTDIR/coverage/.coverage + +# Configure the correct folder on the .coveragerc file +cat $PROJECTDIR/coverage/.coveragerc.template | sed s+~/teraflow/controller+$PROJECTDIR+g > $RCFILE + +# Destroy old coverage file +rm -f $COVERAGEFILE + +# Set the name of the Kubernetes namespace and hostname to use. +K8S_NAMESPACE_D1="oeccpsc22-1" +K8S_NAMESPACE_D2="oeccpsc22-2" +# K8S_HOSTNAME="kubernetes-master" +# dynamically gets the name of the K8s master node +K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'` + +# Flush Context database +kubectl --namespace $K8S_NAMESPACE_D1 exec -it deployment/contextservice --container redis -- redis-cli FLUSHALL +kubectl --namespace $K8S_NAMESPACE_D2 exec -it deployment/contextservice --container redis -- redis-cli FLUSHALL + +export D1_CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export D1_DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') + +export D2_CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export D2_DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') + +# Useful flags for pytest: +#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG + +# Run functional test and analyze coverage of code at same time + +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + tests/oeccpsc22/tests/test_functional_bootstrap.py diff --git a/src/tests/oeccpsc22/run_test_02_create_service.sh b/src/tests/oeccpsc22/run_test_02_create_service.sh new file mode 100755 index 000000000..c01f64741 --- /dev/null +++ b/src/tests/oeccpsc22/run_test_02_create_service.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + + +PROJECTDIR=`pwd` + +cd $PROJECTDIR/src +RCFILE=$PROJECTDIR/coverage/.coveragerc +COVERAGEFILE=$PROJECTDIR/coverage/.coverage + +# Set the name of the Kubernetes namespace and hostname to use. +K8S_NAMESPACE="oeccpsc22" +# dynamically gets the name of the K8s master node +K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'` + +export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') + +# Useful flags for pytest: +#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG + +# Run functional test and analyze coverage of code at same time + +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + tests/oeccpsc22/tests/test_functional_create_service.py diff --git a/src/tests/oeccpsc22/run_test_03_delete_service.sh b/src/tests/oeccpsc22/run_test_03_delete_service.sh new file mode 100755 index 000000000..1782a143b --- /dev/null +++ b/src/tests/oeccpsc22/run_test_03_delete_service.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + + +PROJECTDIR=`pwd` + +cd $PROJECTDIR/src +RCFILE=$PROJECTDIR/coverage/.coveragerc +COVERAGEFILE=$PROJECTDIR/coverage/.coverage + +# Set the name of the Kubernetes namespace and hostname to use. +K8S_NAMESPACE="oeccpsc22" +# dynamically gets the name of the K8s master node +K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'` + +export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') + +# Useful flags for pytest: +#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG + +# Run functional test and analyze coverage of code at same time + +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + tests/oeccpsc22/tests/test_functional_delete_service.py diff --git a/src/tests/oeccpsc22/run_test_04_cleanup.sh b/src/tests/oeccpsc22/run_test_04_cleanup.sh new file mode 100755 index 000000000..14b4024be --- /dev/null +++ b/src/tests/oeccpsc22/run_test_04_cleanup.sh @@ -0,0 +1,41 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + + +PROJECTDIR=`pwd` + +cd $PROJECTDIR/src +RCFILE=$PROJECTDIR/coverage/.coveragerc +COVERAGEFILE=$PROJECTDIR/coverage/.coverage + +# Set the name of the Kubernetes namespace and hostname to use. +K8S_NAMESPACE="oeccpsc22" +# dynamically gets the name of the K8s master node +K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'` + +export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') + +# Useful flags for pytest: +#-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG + +# Run functional test and analyze coverage of code at same time + +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + tests/oeccpsc22/tests/test_functional_cleanup.py diff --git a/src/tests/oeccpsc22/show_deploy.sh b/src/tests/oeccpsc22/show_deploy.sh new file mode 100755 index 000000000..90d691489 --- /dev/null +++ b/src/tests/oeccpsc22/show_deploy.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +# Deploy TeraFlow instance 1 +printf "TeraFlow Instance 1:\n--------------------\n" +export K8S_NAMESPACE="oeccpsc22-1" +kubectl --namespace $K8S_NAMESPACE get all + +printf "\n\n" + +# Deploy TeraFlow instance 2 +printf "TeraFlow Instance 2:\n--------------------\n" +export K8S_NAMESPACE="oeccpsc22-2" +kubectl --namespace $K8S_NAMESPACE get all diff --git a/src/tests/oeccpsc22/tests/.gitignore b/src/tests/oeccpsc22/tests/.gitignore new file mode 100644 index 000000000..76cb708d1 --- /dev/null +++ b/src/tests/oeccpsc22/tests/.gitignore @@ -0,0 +1,2 @@ +# Add here your files containing confidential testbed details such as IP addresses, ports, usernames, passwords, etc. +Credentials.py diff --git a/src/tests/oeccpsc22/tests/Objects_Domain_1.py b/src/tests/oeccpsc22/tests/Objects_Domain_1.py new file mode 100644 index 000000000..af353c6e9 --- /dev/null +++ b/src/tests/oeccpsc22/tests/Objects_Domain_1.py @@ -0,0 +1,121 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID +from common.tools.object_factory.Context import json_context, json_context_id +from common.tools.object_factory.Device import ( + json_device_emulated_connect_rules, json_device_emulated_packet_router_disabled, json_device_id) +from common.tools.object_factory.Link import json_link, json_link_id +from common.tools.object_factory.Topology import json_topology, json_topology_id +from .Tools import get_link_uuid, json_endpoint_ids + +# ----- Context -------------------------------------------------------------------------------------------------------- +D1_CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_UUID) +D1_CONTEXT = json_context(DEFAULT_CONTEXT_UUID) + +# ----- Topology ------------------------------------------------------------------------------------------------------- +D1_TOPOLOGY_ID = json_topology_id(DEFAULT_TOPOLOGY_UUID, context_id=D1_CONTEXT_ID) +D1_TOPOLOGY = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=D1_CONTEXT_ID) + +# ----- Devices -------------------------------------------------------------------------------------------------------- +# Assume all devices have the same architecture of endpoints +DEVICE_ENDPOINT_DEFS = [ + # Trunk ports + ('1/1', '25Gbps', []), ('1/2', '25Gbps', []), ('1/3', '25Gbps', []), ('1/4', '25Gbps', []), + # Inter-domain ports + ('2/1', '100Gbps', []), ('2/2', '100Gbps', []), + # Access ports + ('3/1', '10Gbps', []), ('3/2', '10Gbps', []), ('3/3', '10Gbps', []), ('3/4', '10Gbps', []), + ('3/5', '10Gbps', []), ('3/6', '10Gbps', []), ('3/7', '10Gbps', []), ('3/8', '10Gbps', []), +] + +DEVICE_D1R1_UUID = 'D1-R1' +DEVICE_D1R1_ID = json_device_id(DEVICE_D1R1_UUID) +DEVICE_D1R1 = json_device_emulated_packet_router_disabled(DEVICE_D1R1_UUID) +DEVICE_D1R1_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +DEVICE_D1R2_UUID = 'D1-R2' +DEVICE_D1R2_ID = json_device_id(DEVICE_D1R2_UUID) +DEVICE_D1R2 = json_device_emulated_packet_router_disabled(DEVICE_D1R2_UUID) +DEVICE_D1R2_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +DEVICE_D1R3_UUID = 'D1-R3' +DEVICE_D1R3_ID = json_device_id(DEVICE_D1R3_UUID) +DEVICE_D1R3 = json_device_emulated_packet_router_disabled(DEVICE_D1R3_UUID) +DEVICE_D1R3_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +DEVICE_D1R4_UUID = 'D1-R4' +DEVICE_D1R4_ID = json_device_id(DEVICE_D1R4_UUID) +DEVICE_D1R4 = json_device_emulated_packet_router_disabled(DEVICE_D1R4_UUID) +DEVICE_D1R4_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +# Virtual devices on remote domains +DEVICE_D2R1_UUID = 'D2-R1' +DEVICE_D2R1_ID = json_device_id(DEVICE_D2R1_UUID) +DEVICE_D2R1 = json_device_emulated_packet_router_disabled(DEVICE_D2R1_UUID) +DEVICE_D2R1_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +ENDPOINT_IDS = {} +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R1_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R2_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R3_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R4_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R1_ID, DEVICE_ENDPOINT_DEFS)) + + +# ----- Links ---------------------------------------------------------------------------------------------------------- +# Intra-domain links +LINK_D1R1_D1R2_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R1_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R2_UUID]['1/1']) +LINK_D1R1_D1R2_ID = json_link_id(LINK_D1R1_D1R2_UUID) +LINK_D1R1_D1R2 = json_link(LINK_D1R1_D1R2_UUID, [ + ENDPOINT_IDS[DEVICE_D1R1_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R2_UUID]['1/1']]) + +LINK_D1R2_D1R3_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R2_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R3_UUID]['1/1']) +LINK_D1R2_D1R3_ID = json_link_id(LINK_D1R2_D1R3_UUID) +LINK_D1R2_D1R3 = json_link(LINK_D1R2_D1R3_UUID, [ + ENDPOINT_IDS[DEVICE_D1R2_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R3_UUID]['1/1']]) + +LINK_D1R3_D1R4_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R3_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R4_UUID]['1/1']) +LINK_D1R3_D1R4_ID = json_link_id(LINK_D1R3_D1R4_UUID) +LINK_D1R3_D1R4 = json_link(LINK_D1R3_D1R4_UUID, [ + ENDPOINT_IDS[DEVICE_D1R3_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R4_UUID]['1/1']]) + +LINK_D1R4_D1R1_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R4_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R1_UUID]['1/1']) +LINK_D1R4_D1R1_ID = json_link_id(LINK_D1R4_D1R1_UUID) +LINK_D1R4_D1R1 = json_link(LINK_D1R4_D1R1_UUID, [ + ENDPOINT_IDS[DEVICE_D1R4_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R1_UUID]['1/1']]) + +# Inter-domain links +LINK_D1R4_D2R1_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R4_UUID]['2/1'], ENDPOINT_IDS[DEVICE_D2R1_UUID]['2/1']) +LINK_D1R4_D2R1_ID = json_link_id(LINK_D1R4_D2R1_UUID) +LINK_D1R4_D2R1 = json_link(LINK_D1R4_D2R1_UUID, [ + ENDPOINT_IDS[DEVICE_D1R4_UUID]['2/1'], ENDPOINT_IDS[DEVICE_D2R1_UUID]['2/1']]) + +# ----- Object Collections --------------------------------------------------------------------------------------------- + +D1_CONTEXTS = [D1_CONTEXT] +D1_TOPOLOGIES = [D1_TOPOLOGY] + +D1_DEVICES = [ + (DEVICE_D1R1, DEVICE_D1R1_CONNECT_RULES), + (DEVICE_D1R2, DEVICE_D1R2_CONNECT_RULES), + (DEVICE_D1R3, DEVICE_D1R3_CONNECT_RULES), + (DEVICE_D1R4, DEVICE_D1R4_CONNECT_RULES), + (DEVICE_D2R1, DEVICE_D2R1_CONNECT_RULES), +] + +D1_LINKS = [ + LINK_D1R1_D1R2, LINK_D1R2_D1R3, LINK_D1R3_D1R4, LINK_D1R4_D1R1, + LINK_D1R4_D2R1, +] diff --git a/src/tests/oeccpsc22/tests/Objects_Domain_2.py b/src/tests/oeccpsc22/tests/Objects_Domain_2.py new file mode 100644 index 000000000..f77989250 --- /dev/null +++ b/src/tests/oeccpsc22/tests/Objects_Domain_2.py @@ -0,0 +1,121 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID +from common.tools.object_factory.Context import json_context, json_context_id +from common.tools.object_factory.Device import ( + json_device_emulated_connect_rules, json_device_emulated_packet_router_disabled, json_device_id) +from common.tools.object_factory.Link import json_link, json_link_id +from common.tools.object_factory.Topology import json_topology, json_topology_id +from .Tools import get_link_uuid, json_endpoint_ids + +# ----- Context -------------------------------------------------------------------------------------------------------- +D2_CONTEXT_ID = json_context_id(DEFAULT_CONTEXT_UUID) +D2_CONTEXT = json_context(DEFAULT_CONTEXT_UUID) + +# ----- Topology ------------------------------------------------------------------------------------------------------- +D2_TOPOLOGY_ID = json_topology_id(DEFAULT_TOPOLOGY_UUID, context_id=D2_CONTEXT_ID) +D2_TOPOLOGY = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=D2_CONTEXT_ID) + +# ----- Devices -------------------------------------------------------------------------------------------------------- +# Assume all devices have the same architecture of endpoints +DEVICE_ENDPOINT_DEFS = [ + # Trunk ports + ('1/1', '25Gbps', []), ('1/2', '25Gbps', []), ('1/3', '25Gbps', []), ('1/4', '25Gbps', []), + # Inter-domain ports + ('2/1', '100Gbps', []), ('2/2', '100Gbps', []), + # Access ports + ('3/1', '10Gbps', []), ('3/2', '10Gbps', []), ('3/3', '10Gbps', []), ('3/4', '10Gbps', []), + ('3/5', '10Gbps', []), ('3/6', '10Gbps', []), ('3/7', '10Gbps', []), ('3/8', '10Gbps', []), +] + +DEVICE_D2R1_UUID = 'D2-R1' +DEVICE_D2R1_ID = json_device_id(DEVICE_D2R1_UUID) +DEVICE_D2R1 = json_device_emulated_packet_router_disabled(DEVICE_D2R1_UUID) +DEVICE_D2R1_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +DEVICE_D2R2_UUID = 'D2-R2' +DEVICE_D2R2_ID = json_device_id(DEVICE_D2R2_UUID) +DEVICE_D2R2 = json_device_emulated_packet_router_disabled(DEVICE_D2R2_UUID) +DEVICE_D2R2_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +DEVICE_D2R3_UUID = 'D2-R3' +DEVICE_D2R3_ID = json_device_id(DEVICE_D2R3_UUID) +DEVICE_D2R3 = json_device_emulated_packet_router_disabled(DEVICE_D2R3_UUID) +DEVICE_D2R3_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +DEVICE_D2R4_UUID = 'D2-R4' +DEVICE_D2R4_ID = json_device_id(DEVICE_D2R4_UUID) +DEVICE_D2R4 = json_device_emulated_packet_router_disabled(DEVICE_D2R4_UUID) +DEVICE_D2R4_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +# Virtual devices on remote domains +DEVICE_D1R1_UUID = 'D1-R1' +DEVICE_D1R1_ID = json_device_id(DEVICE_D1R1_UUID) +DEVICE_D1R1 = json_device_emulated_packet_router_disabled(DEVICE_D1R1_UUID) +DEVICE_D1R1_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) + +ENDPOINT_IDS = {} +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R1_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R2_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R3_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R4_ID, DEVICE_ENDPOINT_DEFS)) +ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R1_ID, DEVICE_ENDPOINT_DEFS)) + + +# ----- Links ---------------------------------------------------------------------------------------------------------- +# Intra-domain links +LINK_D2R1_D2R2_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R1_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R2_UUID]['1/1']) +LINK_D2R1_D2R2_ID = json_link_id(LINK_D2R1_D2R2_UUID) +LINK_D2R1_D2R2 = json_link(LINK_D2R1_D2R2_UUID, [ + ENDPOINT_IDS[DEVICE_D2R1_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R2_UUID]['1/1']]) + +LINK_D2R2_D2R3_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R2_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R3_UUID]['1/1']) +LINK_D2R2_D2R3_ID = json_link_id(LINK_D2R2_D2R3_UUID) +LINK_D2R2_D2R3 = json_link(LINK_D2R2_D2R3_UUID, [ + ENDPOINT_IDS[DEVICE_D2R2_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R3_UUID]['1/1']]) + +LINK_D2R3_D2R4_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R3_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R4_UUID]['1/1']) +LINK_D2R3_D2R4_ID = json_link_id(LINK_D2R3_D2R4_UUID) +LINK_D2R3_D2R4 = json_link(LINK_D2R3_D2R4_UUID, [ + ENDPOINT_IDS[DEVICE_D2R3_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R4_UUID]['1/1']]) + +LINK_D2R4_D2R1_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R4_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R1_UUID]['1/1']) +LINK_D2R4_D2R1_ID = json_link_id(LINK_D2R4_D2R1_UUID) +LINK_D2R4_D2R1 = json_link(LINK_D2R4_D2R1_UUID, [ + ENDPOINT_IDS[DEVICE_D2R4_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R1_UUID]['1/1']]) + +# Inter-domain links +LINK_D2R4_D1R1_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R4_UUID]['2/1'], ENDPOINT_IDS[DEVICE_D1R1_UUID]['2/1']) +LINK_D2R4_D1R1_ID = json_link_id(LINK_D2R4_D1R1_UUID) +LINK_D2R4_D1R1 = json_link(LINK_D2R4_D1R1_UUID, [ + ENDPOINT_IDS[DEVICE_D2R4_UUID]['2/1'], ENDPOINT_IDS[DEVICE_D1R1_UUID]['2/1']]) + +# ----- Object Collections --------------------------------------------------------------------------------------------- + +D2_CONTEXTS = [D2_CONTEXT] +D2_TOPOLOGIES = [D2_TOPOLOGY] + +D2_DEVICES = [ + (DEVICE_D2R1, DEVICE_D2R1_CONNECT_RULES), + (DEVICE_D2R2, DEVICE_D2R2_CONNECT_RULES), + (DEVICE_D2R3, DEVICE_D2R3_CONNECT_RULES), + (DEVICE_D2R4, DEVICE_D2R4_CONNECT_RULES), + (DEVICE_D1R1, DEVICE_D1R1_CONNECT_RULES), +] + +D2_LINKS = [ + LINK_D2R1_D2R2, LINK_D2R2_D2R3, LINK_D2R3_D2R4, LINK_D2R4_D2R1, + LINK_D2R4_D1R1, +] diff --git a/src/tests/oeccpsc22/tests/Objects_Service.py b/src/tests/oeccpsc22/tests/Objects_Service.py new file mode 100644 index 000000000..b9ec2a691 --- /dev/null +++ b/src/tests/oeccpsc22/tests/Objects_Service.py @@ -0,0 +1,35 @@ + + +# ----- WIM Service Settings ------------------------------------------------------------------------------------------- +WIM_SEP_R1_ID = compose_service_endpoint_id(ENDPOINT_ID_R1_13_1_2) +WIM_SEP_R1_ROUTER_ID = '10.10.10.1' +WIM_SEP_R1_ROUTER_DIST = '65000:111' +WIM_SEP_R1_SITE_ID = '1' +WIM_SEP_R1_BEARER = compose_bearer(ENDPOINT_ID_R1_13_1_2, WIM_SEP_R1_ROUTER_ID, WIM_SEP_R1_ROUTER_DIST) +WIM_SRV_R1_VLAN_ID = 400 + +WIM_SEP_R3_ID = compose_service_endpoint_id(ENDPOINT_ID_R3_13_1_2) +WIM_SEP_R3_ROUTER_ID = '20.20.20.1' +WIM_SEP_R3_ROUTER_DIST = '65000:222' +WIM_SEP_R3_SITE_ID = '2' +WIM_SEP_R3_BEARER = compose_bearer(ENDPOINT_ID_R3_13_1_2, WIM_SEP_R3_ROUTER_ID, WIM_SEP_R3_ROUTER_DIST) +WIM_SRV_R3_VLAN_ID = 500 + +WIM_USERNAME = 'admin' +WIM_PASSWORD = 'admin' + +WIM_MAPPING = [ + {'device-id': DEVICE_R1_UUID, 'service_endpoint_id': WIM_SEP_R1_ID, + 'service_mapping_info': {'bearer': {'bearer-reference': WIM_SEP_R1_BEARER}, 'site-id': WIM_SEP_R1_SITE_ID}}, + {'device-id': DEVICE_R3_UUID, 'service_endpoint_id': WIM_SEP_R3_ID, + 'service_mapping_info': {'bearer': {'bearer-reference': WIM_SEP_R3_BEARER}, 'site-id': WIM_SEP_R3_SITE_ID}}, +] +WIM_SERVICE_TYPE = 'ELINE' +WIM_SERVICE_CONNECTION_POINTS = [ + {'service_endpoint_id': WIM_SEP_R1_ID, + 'service_endpoint_encapsulation_type': 'dot1q', + 'service_endpoint_encapsulation_info': {'vlan': WIM_SRV_R1_VLAN_ID}}, + {'service_endpoint_id': WIM_SEP_R3_ID, + 'service_endpoint_encapsulation_type': 'dot1q', + 'service_endpoint_encapsulation_info': {'vlan': WIM_SRV_R3_VLAN_ID}}, +] diff --git a/src/tests/oeccpsc22/tests/Tools.py b/src/tests/oeccpsc22/tests/Tools.py new file mode 100644 index 000000000..a782b6bb3 --- /dev/null +++ b/src/tests/oeccpsc22/tests/Tools.py @@ -0,0 +1,25 @@ +from typing import Dict, List, Tuple +from common.tools.object_factory.EndPoint import json_endpoint_id + +def json_endpoint_ids(device_id : Dict, endpoint_descriptors : List[Tuple[str, str, List[int]]]): + return { + device_id['device_uuid']['uuid']: { + ep_uuid: json_endpoint_id(device_id, ep_uuid, topology_id=None) + for ep_uuid, _, _ in endpoint_descriptors + } + } + +def get_link_uuid(a_endpoint_id : Dict, z_endpoint_id : Dict) -> str: + return '{:s}/{:s}=={:s}/{:s}'.format( + a_endpoint_id['device_id']['device_uuid']['uuid'], a_endpoint_id['endpoint_uuid']['uuid'], + a_endpoint_id['device_id']['device_uuid']['uuid'], z_endpoint_id['endpoint_uuid']['uuid']) + +def compose_service_endpoint_id(endpoint_id): + device_uuid = endpoint_id['device_id']['device_uuid']['uuid'] + endpoint_uuid = endpoint_id['endpoint_uuid']['uuid'] + return ':'.join([device_uuid, endpoint_uuid]) + +def compose_bearer(endpoint_id): + device_uuid = endpoint_id['device_id']['device_uuid']['uuid'] + endpoint_uuid = endpoint_id['endpoint_uuid']['uuid'] + return ':'.join([device_uuid, endpoint_uuid]) diff --git a/src/tests/oeccpsc22/tests/__init__.py b/src/tests/oeccpsc22/tests/__init__.py new file mode 100644 index 000000000..70a332512 --- /dev/null +++ b/src/tests/oeccpsc22/tests/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/tests/oeccpsc22/tests/test_functional_bootstrap.py b/src/tests/oeccpsc22/tests/test_functional_bootstrap.py new file mode 100644 index 000000000..b09b558cd --- /dev/null +++ b/src/tests/oeccpsc22/tests/test_functional_bootstrap.py @@ -0,0 +1,208 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 copy, logging, pytest +from common.Settings import get_setting +from common.tests.EventTools import EVENT_CREATE, check_events +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.Link import json_link_id +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from context.proto.context_pb2 import Context, ContextId, Device, Empty, Link, Topology +from device.client.DeviceClient import DeviceClient +from .Objects_Domain_1 import D1_CONTEXT_ID, D1_CONTEXTS, D1_DEVICES, D1_LINKS, D1_TOPOLOGIES +from .Objects_Domain_2 import D2_CONTEXT_ID, D2_CONTEXTS, D2_DEVICES, D2_LINKS, D2_TOPOLOGIES + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +@pytest.fixture(scope='session') +def d1_context_client(): + _client = ContextClient( + get_setting('D1_CONTEXTSERVICE_SERVICE_HOST'), get_setting('D1_CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + +@pytest.fixture(scope='session') +def d1_device_client(): + _client = DeviceClient( + get_setting('D1_DEVICESERVICE_SERVICE_HOST'), get_setting('D1_DEVICESERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + +@pytest.fixture(scope='session') +def d2_context_client(): + _client = ContextClient( + get_setting('D2_CONTEXTSERVICE_SERVICE_HOST'), get_setting('D2_CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + +@pytest.fixture(scope='session') +def d2_device_client(): + _client = DeviceClient( + get_setting('D2_DEVICESERVICE_SERVICE_HOST'), get_setting('D2_DEVICESERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +def test_scenario_empty( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(context_client): + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == 0 + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == 0 + + response = context_client.ListLinks(Empty()) + assert len(response.links) == 0 + + # ----- List entities - Ensure database is empty ------------------------------------------------------------------- + per_domain(d1_context_client) + per_domain(d2_context_client) + + +def test_prepare_scenario( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(contexts, topologies, context_client): + for context in contexts: + context_uuid = context['context_id']['context_uuid']['uuid'] + LOGGER.info('Adding Context {:s}'.format(context_uuid)) + response = context_client.SetContext(Context(**context)) + assert response.context_uuid.uuid == context_uuid + + for topology in topologies: + context_uuid = topology['topology_id']['context_id']['context_uuid']['uuid'] + topology_uuid = topology['topology_id']['topology_uuid']['uuid'] + LOGGER.info('Adding Topology {:s}/{:s}'.format(context_uuid, topology_uuid)) + response = context_client.SetTopology(Topology(**topology)) + assert response.context_id.context_uuid.uuid == context_uuid + assert response.topology_uuid.uuid == topology_uuid + + # ----- Create Contexts and Topologies ----------------------------------------------------------------------------- + per_domain(D1_CONTEXTS, D1_TOPOLOGIES, d1_context_client) + per_domain(D2_CONTEXTS, D2_TOPOLOGIES, d2_context_client) + + +def test_scenario_ready( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(contexts, topologies, context_id, context_client): + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(contexts) + + response = context_client.ListTopologies(ContextId(**context_id)) + assert len(response.topologies) == len(topologies) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == 0 + + response = context_client.ListLinks(Empty()) + assert len(response.links) == 0 + + response = context_client.ListServices(ContextId(**context_id)) + assert len(response.services) == 0 + + # ----- List entities - Ensure scenario is ready ------------------------------------------------------------------- + per_domain(D1_CONTEXTS, D1_TOPOLOGIES, D1_CONTEXT_ID, d1_context_client) + per_domain(D2_CONTEXTS, D2_TOPOLOGIES, D2_CONTEXT_ID, d2_context_client) + + +def test_devices_bootstraping( + d1_device_client : DeviceClient, # pylint: disable=redefined-outer-name + d2_device_client : DeviceClient): # pylint: disable=redefined-outer-name + + def per_domain(devices, device_client): + for device, connect_rules in devices: + device_uuid = device['device_id']['device_uuid']['uuid'] + LOGGER.info('Adding Device {:s}'.format(device_uuid)) + device_with_connect_rules = copy.deepcopy(device) + device_with_connect_rules['device_config']['config_rules'].extend(connect_rules) + response = device_client.AddDevice(Device(**device_with_connect_rules)) + assert response.device_uuid.uuid == device_uuid + + # ----- Create Devices and Validate Collected Events --------------------------------------------------------------- + per_domain(D1_DEVICES, d1_device_client) + per_domain(D2_DEVICES, d2_device_client) + + +def test_devices_bootstrapped( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(contexts, topologies, devices, context_id, context_client): + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(contexts) + + response = context_client.ListTopologies(ContextId(**context_id)) + assert len(response.topologies) == len(topologies) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(devices) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == 0 + + response = context_client.ListServices(ContextId(**context_id)) + assert len(response.services) == 0 + + # ----- List entities - Ensure bevices are created ----------------------------------------------------------------- + per_domain(D1_CONTEXTS, D1_TOPOLOGIES, D1_DEVICES, D1_CONTEXT_ID, d1_context_client) + per_domain(D2_CONTEXTS, D2_TOPOLOGIES, D2_DEVICES, D2_CONTEXT_ID, d2_context_client) + + +def test_links_creation( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(links, context_client): + for link in links: + link_uuid = link['link_id']['link_uuid']['uuid'] + LOGGER.info('Adding Link {:s}'.format(link_uuid)) + response = context_client.SetLink(Link(**link)) + assert response.link_uuid.uuid == link_uuid + + # ----- Create Links and Validate Collected Events ----------------------------------------------------------------- + per_domain(D1_LINKS, d1_context_client) + per_domain(D2_LINKS, d2_context_client) + + +def test_links_created( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(contexts, topologies, devices, links, context_id, context_client): + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(contexts) + + response = context_client.ListTopologies(ContextId(**context_id)) + assert len(response.topologies) == len(topologies) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(devices) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(links) + + response = context_client.ListServices(ContextId(**context_id)) + assert len(response.services) == 0 + + # ----- List entities - Ensure links are created ------------------------------------------------------------------- + per_domain(D1_CONTEXTS, D1_TOPOLOGIES, D1_DEVICES, D1_LINKS, D1_CONTEXT_ID, d1_context_client) + per_domain(D2_CONTEXTS, D2_TOPOLOGIES, D2_DEVICES, D2_LINKS, D2_CONTEXT_ID, d2_context_client) diff --git a/src/tests/oeccpsc22/tests/test_functional_cleanup.py b/src/tests/oeccpsc22/tests/test_functional_cleanup.py new file mode 100644 index 000000000..eb78a5850 --- /dev/null +++ b/src/tests/oeccpsc22/tests/test_functional_cleanup.py @@ -0,0 +1,123 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, pytest +from common.Settings import get_setting +from common.tests.EventTools import EVENT_REMOVE, check_events +from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.Link import json_link_id +from common.tools.object_factory.Topology import json_topology_id +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from context.proto.context_pb2 import ContextId, DeviceId, Empty, LinkId, TopologyId +from device.client.DeviceClient import DeviceClient +from .Objects import CONTEXT_ID, CONTEXTS, DEVICES, LINKS, TOPOLOGIES + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + + +@pytest.fixture(scope='session') +def context_client(): + _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +@pytest.fixture(scope='session') +def device_client(): + _client = DeviceClient(get_setting('DEVICESERVICE_SERVICE_HOST'), get_setting('DEVICESERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +def test_services_removed(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure service is removed ------------------------------------------------------------------ + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(CONTEXTS) + + response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == len(TOPOLOGIES) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(DEVICES) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(LINKS) + + response = context_client.ListServices(ContextId(**CONTEXT_ID)) + assert len(response.services) == 0 + + +def test_scenario_cleanup( + context_client : ContextClient, device_client : DeviceClient): # pylint: disable=redefined-outer-name + + # ----- Start the EventsCollector ---------------------------------------------------------------------------------- + events_collector = EventsCollector(context_client) + events_collector.start() + + expected_events = [] + + # ----- Delete Links and Validate Collected Events ----------------------------------------------------------------- + for link in LINKS: + link_id = link['link_id'] + link_uuid = link_id['link_uuid']['uuid'] + LOGGER.info('Deleting Link {:s}'.format(link_uuid)) + context_client.RemoveLink(LinkId(**link_id)) + expected_events.append(('LinkEvent', EVENT_REMOVE, json_link_id(link_uuid))) + + # ----- Delete Devices and Validate Collected Events --------------------------------------------------------------- + for device, _ in DEVICES: + device_id = device['device_id'] + device_uuid = device_id['device_uuid']['uuid'] + LOGGER.info('Deleting Device {:s}'.format(device_uuid)) + device_client.DeleteDevice(DeviceId(**device_id)) + expected_events.append(('DeviceEvent', EVENT_REMOVE, json_device_id(device_uuid))) + + # ----- Delete Topologies and Validate Collected Events ------------------------------------------------------------ + for topology in TOPOLOGIES: + topology_id = topology['topology_id'] + context_uuid = topology_id['context_id']['context_uuid']['uuid'] + topology_uuid = topology_id['topology_uuid']['uuid'] + LOGGER.info('Deleting Topology {:s}/{:s}'.format(context_uuid, topology_uuid)) + context_client.RemoveTopology(TopologyId(**topology_id)) + context_id = json_context_id(context_uuid) + expected_events.append(('TopologyEvent', EVENT_REMOVE, json_topology_id(topology_uuid, context_id=context_id))) + + # ----- Delete Contexts and Validate Collected Events -------------------------------------------------------------- + for context in CONTEXTS: + context_id = context['context_id'] + context_uuid = context_id['context_uuid']['uuid'] + LOGGER.info('Deleting Context {:s}'.format(context_uuid)) + context_client.RemoveContext(ContextId(**context_id)) + expected_events.append(('ContextEvent', EVENT_REMOVE, json_context_id(context_uuid))) + + # ----- Validate Collected Events ---------------------------------------------------------------------------------- + check_events(events_collector, expected_events) + + # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- + events_collector.stop() + + +def test_scenario_empty_again(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure database is empty again ------------------------------------------------------------- + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == 0 + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == 0 + + response = context_client.ListLinks(Empty()) + assert len(response.links) == 0 diff --git a/src/tests/oeccpsc22/tests/test_functional_create_service.py b/src/tests/oeccpsc22/tests/test_functional_create_service.py new file mode 100644 index 000000000..f3389fdbf --- /dev/null +++ b/src/tests/oeccpsc22/tests/test_functional_create_service.py @@ -0,0 +1,129 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, pytest +from common.DeviceTypes import DeviceTypeEnum +from common.Settings import get_setting +from common.tests.EventTools import EVENT_CREATE, EVENT_UPDATE, check_events +from common.tools.object_factory.Connection import json_connection_id +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.Service import json_service_id +from common.tools.grpc.Tools import grpc_message_to_json_string +from compute.tests.mock_osm.MockOSM import MockOSM +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from context.proto.context_pb2 import ContextId, Empty +from .Objects import ( + CONTEXT_ID, CONTEXTS, DEVICE_O1_UUID, DEVICE_R1_UUID, DEVICE_R3_UUID, DEVICES, LINKS, TOPOLOGIES, + WIM_MAPPING, WIM_PASSWORD, WIM_SERVICE_CONNECTION_POINTS, WIM_SERVICE_TYPE, WIM_USERNAME) + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +DEVTYPE_EMU_PR = DeviceTypeEnum.EMULATED_PACKET_ROUTER.value +DEVTYPE_EMU_OLS = DeviceTypeEnum.EMULATED_OPTICAL_LINE_SYSTEM.value + + +@pytest.fixture(scope='session') +def context_client(): + _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +@pytest.fixture(scope='session') +def osm_wim(): + wim_url = 'http://{:s}:{:s}'.format( + get_setting('COMPUTESERVICE_SERVICE_HOST'), str(get_setting('COMPUTESERVICE_SERVICE_PORT_HTTP'))) + return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD) + + +def test_scenario_is_correct(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure links are created ------------------------------------------------------------------- + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(CONTEXTS) + + response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == len(TOPOLOGIES) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(DEVICES) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(LINKS) + + response = context_client.ListServices(ContextId(**CONTEXT_ID)) + assert len(response.services) == 0 + + +def test_service_creation(context_client : ContextClient, osm_wim : MockOSM): # pylint: disable=redefined-outer-name + # ----- Start the EventsCollector ---------------------------------------------------------------------------------- + events_collector = EventsCollector(context_client, log_events_received=True) + events_collector.start() + + # ----- Create Service --------------------------------------------------------------------------------------------- + service_uuid = osm_wim.create_connectivity_service(WIM_SERVICE_TYPE, WIM_SERVICE_CONNECTION_POINTS) + osm_wim.get_connectivity_service_status(service_uuid) + + # ----- Validate collected events ---------------------------------------------------------------------------------- + + packet_connection_uuid = '{:s}:{:s}'.format(service_uuid, DEVTYPE_EMU_PR) + optical_connection_uuid = '{:s}:optical:{:s}'.format(service_uuid, DEVTYPE_EMU_OLS) + optical_service_uuid = '{:s}:optical'.format(service_uuid) + + expected_events = [ + # Create packet service and add first endpoint + ('ServiceEvent', EVENT_CREATE, json_service_id(service_uuid, context_id=CONTEXT_ID)), + ('ServiceEvent', EVENT_UPDATE, json_service_id(service_uuid, context_id=CONTEXT_ID)), + + # Configure OLS controller, create optical service, create optical connection + ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_O1_UUID)), + ('ServiceEvent', EVENT_CREATE, json_service_id(optical_service_uuid, context_id=CONTEXT_ID)), + ('ConnectionEvent', EVENT_CREATE, json_connection_id(optical_connection_uuid)), + + # Configure endpoint packet devices, add second endpoint to service, create connection + ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_R1_UUID)), + ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_R3_UUID)), + ('ServiceEvent', EVENT_UPDATE, json_service_id(service_uuid, context_id=CONTEXT_ID)), + ('ConnectionEvent', EVENT_CREATE, json_connection_id(packet_connection_uuid)), + ] + check_events(events_collector, expected_events) + + # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- + events_collector.stop() + + +def test_scenario_service_created(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure service is created ------------------------------------------------------------------ + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(CONTEXTS) + + response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == len(TOPOLOGIES) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(DEVICES) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(LINKS) + + response = context_client.ListServices(ContextId(**CONTEXT_ID)) + LOGGER.info('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) + assert len(response.services) == 2 # L3NM + TAPI + for service in response.services: + service_id = service.service_id + response = context_client.ListConnections(service_id) + LOGGER.info(' ServiceId[{:s}] => Connections[{:d}] = {:s}'.format( + grpc_message_to_json_string(service_id), len(response.connections), grpc_message_to_json_string(response))) + assert len(response.connections) == 1 # one connection per service diff --git a/src/tests/oeccpsc22/tests/test_functional_delete_service.py b/src/tests/oeccpsc22/tests/test_functional_delete_service.py new file mode 100644 index 000000000..51e91a596 --- /dev/null +++ b/src/tests/oeccpsc22/tests/test_functional_delete_service.py @@ -0,0 +1,134 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, pytest +from common.DeviceTypes import DeviceTypeEnum +from common.Settings import get_setting +from common.tests.EventTools import EVENT_REMOVE, EVENT_UPDATE, check_events +from common.tools.object_factory.Connection import json_connection_id +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.Service import json_service_id +from common.tools.grpc.Tools import grpc_message_to_json_string +from compute.tests.mock_osm.MockOSM import MockOSM +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from context.proto.context_pb2 import ContextId, Empty +from .Objects import ( + CONTEXT_ID, CONTEXTS, DEVICE_O1_UUID, DEVICE_R1_UUID, DEVICE_R3_UUID, DEVICES, LINKS, TOPOLOGIES, WIM_MAPPING, + WIM_PASSWORD, WIM_USERNAME) + + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +DEVTYPE_EMU_PR = DeviceTypeEnum.EMULATED_PACKET_ROUTER.value +DEVTYPE_EMU_OLS = DeviceTypeEnum.EMULATED_OPTICAL_LINE_SYSTEM.value + + +@pytest.fixture(scope='session') +def context_client(): + _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +@pytest.fixture(scope='session') +def osm_wim(): + wim_url = 'http://{:s}:{:s}'.format( + get_setting('COMPUTESERVICE_SERVICE_HOST'), str(get_setting('COMPUTESERVICE_SERVICE_PORT_HTTP'))) + return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD) + + +def test_scenario_is_correct(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure service is created ------------------------------------------------------------------ + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(CONTEXTS) + + response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == len(TOPOLOGIES) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(DEVICES) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(LINKS) + + response = context_client.ListServices(ContextId(**CONTEXT_ID)) + LOGGER.info('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) + assert len(response.services) == 2 # L3NM + TAPI + for service in response.services: + service_id = service.service_id + response = context_client.ListConnections(service_id) + LOGGER.info(' ServiceId[{:s}] => Connections[{:d}] = {:s}'.format( + grpc_message_to_json_string(service_id), len(response.connections), grpc_message_to_json_string(response))) + assert len(response.connections) == 1 # one connection per service + + +def test_service_removal(context_client : ContextClient, osm_wim : MockOSM): # pylint: disable=redefined-outer-name + # ----- Start the EventsCollector ---------------------------------------------------------------------------------- + events_collector = EventsCollector(context_client, log_events_received=True) + events_collector.start() + + # ----- Delete Service --------------------------------------------------------------------------------------------- + response = context_client.ListServiceIds(ContextId(**CONTEXT_ID)) + LOGGER.info('Services[{:d}] = {:s}'.format(len(response.service_ids), grpc_message_to_json_string(response))) + assert len(response.service_ids) == 2 # L3NM + TAPI + service_uuids = set() + for service_id in response.service_ids: + service_uuid = service_id.service_uuid.uuid + if service_uuid.endswith(':optical'): continue + service_uuids.add(service_uuid) + osm_wim.conn_info[service_uuid] = {} + + assert len(service_uuids) == 1 # assume a single service has been created + service_uuid = set(service_uuids).pop() + + osm_wim.delete_connectivity_service(service_uuid) + + # ----- Validate collected events ---------------------------------------------------------------------------------- + packet_connection_uuid = '{:s}:{:s}'.format(service_uuid, DEVTYPE_EMU_PR) + optical_connection_uuid = '{:s}:optical:{:s}'.format(service_uuid, DEVTYPE_EMU_OLS) + optical_service_uuid = '{:s}:optical'.format(service_uuid) + + expected_events = [ + ('ConnectionEvent', EVENT_REMOVE, json_connection_id(packet_connection_uuid)), + ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_R1_UUID)), + ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_R3_UUID)), + ('ServiceEvent', EVENT_REMOVE, json_service_id(service_uuid, context_id=CONTEXT_ID)), + ('ConnectionEvent', EVENT_REMOVE, json_connection_id(optical_connection_uuid)), + ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_O1_UUID)), + ('ServiceEvent', EVENT_REMOVE, json_service_id(optical_service_uuid, context_id=CONTEXT_ID)), + ] + check_events(events_collector, expected_events) + + # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- + events_collector.stop() + + +def test_services_removed(context_client : ContextClient): # pylint: disable=redefined-outer-name + # ----- List entities - Ensure service is removed ------------------------------------------------------------------ + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(CONTEXTS) + + response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == len(TOPOLOGIES) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(DEVICES) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(LINKS) + + response = context_client.ListServices(ContextId(**CONTEXT_ID)) + assert len(response.services) == 0 -- GitLab From 1d7e485e37d2a899c003ed1240c00525aa61bead Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Wed, 9 Mar 2022 18:41:18 +0100 Subject: [PATCH 005/353] Extended deploy script to configure dashboards only when WebUI component is included in the deploy. --- deploy_in_kubernetes.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/deploy_in_kubernetes.sh b/deploy_in_kubernetes.sh index 0da87dbe5..a1b4551dd 100755 --- a/deploy_in_kubernetes.sh +++ b/deploy_in_kubernetes.sh @@ -131,9 +131,12 @@ for COMPONENT in $COMPONENTS; do printf "\n" done -echo "Configuring DataStores and Dashboards..." -./configure_dashboards.sh -printf "\n\n" + +if [[ "$COMPONENTS" == *"webui"* ]]; then + echo "Configuring WebUI DataStores and Dashboards..." + ./configure_dashboards.sh + printf "\n\n" +fi echo "Reporting Deployment..." kubectl --namespace $K8S_NAMESPACE get all -- GitLab From da137f1ff3e1bbff6fc4ae5c0edaa73ede44855d Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Wed, 9 Mar 2022 19:57:58 +0100 Subject: [PATCH 006/353] Corrected license headers for genproto.sh scripts --- src/compute/genproto.sh | 4 +--- src/context/genproto.sh | 4 +--- src/dbscanserving/genproto.sh | 4 +--- src/device/genproto.sh | 4 +--- src/interdomain/genproto.sh | 16 ++++++++-------- src/l3_attackmitigator/genproto.sh | 4 +--- src/l3_centralizedattackdetector/genproto.sh | 4 +--- src/l3_distributedattackdetector/genproto.sh | 4 +--- src/monitoring/genproto.sh | 2 +- src/opticalattackmitigator/genproto.sh | 4 +--- src/opticalcentralizedattackdetector/genproto.sh | 4 +--- src/service/genproto.sh | 4 +--- src/slice/genproto.sh | 7 ++++--- src/webui/genproto.sh | 4 +--- 14 files changed, 24 insertions(+), 45 deletions(-) diff --git a/src/compute/genproto.sh b/src/compute/genproto.sh index c991aaf01..27ad91b29 100755 --- a/src/compute/genproto.sh +++ b/src/compute/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ # 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) diff --git a/src/context/genproto.sh b/src/context/genproto.sh index 8302d3550..7d3a0afc5 100755 --- a/src/context/genproto.sh +++ b/src/context/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ # 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) diff --git a/src/dbscanserving/genproto.sh b/src/dbscanserving/genproto.sh index d44156c2f..452324ad7 100755 --- a/src/dbscanserving/genproto.sh +++ b/src/dbscanserving/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ # 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) diff --git a/src/device/genproto.sh b/src/device/genproto.sh index 31632fb89..795fe4bba 100755 --- a/src/device/genproto.sh +++ b/src/device/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ # 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) diff --git a/src/interdomain/genproto.sh b/src/interdomain/genproto.sh index e85797797..138b8d640 100755 --- a/src/interdomain/genproto.sh +++ b/src/interdomain/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ # 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) @@ -24,14 +22,16 @@ 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 kpi_sample_types.proto python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto interdomain.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/interdomain_pb2_grpc.py +rm proto/kpi_sample_types_pb2_grpc.py +rm proto/slice_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/service_pb2_grpc.py +sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/kpi_sample_types_pb2.py sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/interdomain_pb2.py - +sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/interdomain_pb2_grpc.py +sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/slice_pb2.py diff --git a/src/l3_attackmitigator/genproto.sh b/src/l3_attackmitigator/genproto.sh index 40635e44a..916b6b548 100644 --- a/src/l3_attackmitigator/genproto.sh +++ b/src/l3_attackmitigator/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ # 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) diff --git a/src/l3_centralizedattackdetector/genproto.sh b/src/l3_centralizedattackdetector/genproto.sh index a68c8638d..d9f624fae 100644 --- a/src/l3_centralizedattackdetector/genproto.sh +++ b/src/l3_centralizedattackdetector/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ # 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) diff --git a/src/l3_distributedattackdetector/genproto.sh b/src/l3_distributedattackdetector/genproto.sh index 9ca93e30e..d67316784 100644 --- a/src/l3_distributedattackdetector/genproto.sh +++ b/src/l3_distributedattackdetector/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ # 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) diff --git a/src/monitoring/genproto.sh b/src/monitoring/genproto.sh index b37b49ba2..4f92cd730 100755 --- a/src/monitoring/genproto.sh +++ b/src/monitoring/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/src/opticalattackmitigator/genproto.sh b/src/opticalattackmitigator/genproto.sh index 9f0651441..feace22dc 100755 --- a/src/opticalattackmitigator/genproto.sh +++ b/src/opticalattackmitigator/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ # 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) diff --git a/src/opticalcentralizedattackdetector/genproto.sh b/src/opticalcentralizedattackdetector/genproto.sh index 76df9bf83..10351cd3d 100755 --- a/src/opticalcentralizedattackdetector/genproto.sh +++ b/src/opticalcentralizedattackdetector/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ # 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) diff --git a/src/service/genproto.sh b/src/service/genproto.sh index 7ea496d6f..3e147eb94 100755 --- a/src/service/genproto.sh +++ b/src/service/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ # 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) diff --git a/src/slice/genproto.sh b/src/slice/genproto.sh index b2e38b43a..daa5dbb98 100755 --- a/src/slice/genproto.sh +++ b/src/slice/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ # 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) @@ -24,13 +22,16 @@ 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 kpi_sample_types.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/kpi_sample_types_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/kpi_sample_types_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 diff --git a/src/webui/genproto.sh b/src/webui/genproto.sh index 18a0d4f92..28b0ef9ea 100755 --- a/src/webui/genproto.sh +++ b/src/webui/genproto.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,8 +14,6 @@ # 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) -- GitLab From 9d45ed64f3b216fec05a89d38469cb2e7f31b4e2 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Wed, 9 Mar 2022 19:58:50 +0100 Subject: [PATCH 007/353] Formatted proto interdomain and slice files --- proto/interdomain.proto | 16 ++++------------ proto/slice.proto | 5 ++--- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/proto/interdomain.proto b/proto/interdomain.proto index 7088586e2..80fe07469 100644 --- a/proto/interdomain.proto +++ b/proto/interdomain.proto @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -//Example of topology syntax = "proto3"; package interdomain; @@ -20,15 +19,8 @@ import "context.proto"; import "slice.proto"; service InterdomainService { - rpc Authenticate (context.TeraFlowController) returns (context.AuthenticationResult) {} - rpc LookUpSlice(slice.TransportSlice) returns (slice.SliceId) {} //Slice component or from interdomain component - rpc OrderSliceFromCatalog(slice.TransportSlice) returns (slice.SliceStatus) {} - rpc CreateSliceAndAddToCatalog(slice.TransportSlice) returns (slice.SliceStatus) {} + rpc Authenticate (context.TeraFlowController) returns (context.AuthenticationResult) {} + rpc LookUpSlice (slice.TransportSlice ) returns (slice.SliceId ) {} + rpc OrderSliceFromCatalog (slice.TransportSlice ) returns (slice.SliceStatus ) {} + rpc CreateSliceAndAddToCatalog(slice.TransportSlice ) returns (slice.SliceStatus ) {} } - - - - - - - diff --git a/proto/slice.proto b/proto/slice.proto index 73e945a40..43f3329d4 100644 --- a/proto/slice.proto +++ b/proto/slice.proto @@ -12,15 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//Example of topology syntax = "proto3"; package slice; import "context.proto"; service SliceService { - rpc CreateUpdateSlice (TransportSlice) returns (SliceStatus) {} - rpc DeleteSlice (TransportSlice) returns (context.Empty) {} + rpc CreateUpdateSlice (TransportSlice) returns (SliceStatus ) {} + rpc DeleteSlice (TransportSlice) returns (context.Empty) {} } message SliceEndpoint { -- GitLab From 37f647b9778d56ff160889f16e7cbd13714fa778 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Wed, 9 Mar 2022 19:59:12 +0100 Subject: [PATCH 008/353] Corrected random license headers --- src/common/logger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/logger.py b/src/common/logger.py index a0e9997cd..c90e0bcf3 100644 --- a/src/common/logger.py +++ b/src/common/logger.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -# Copyright 2018 Google LLC +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. -- GitLab From eaee6c8c7399470b5db99bf896a991939e4cf779 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Wed, 9 Mar 2022 19:59:49 +0100 Subject: [PATCH 009/353] Defined/Corrected skeleton for Interdomain component --- src/interdomain/Dockerfile | 3 +- src/interdomain/client/InterdomainClient.py | 9 +- src/interdomain/proto/__init__.py | 14 - ...ce_pb2_grpc.py => interdomain_pb2_grpc.py} | 115 +++--- src/interdomain/proto/kpi_sample_types_pb2.py | 78 +++++ src/interdomain/proto/service_pb2.py | 88 ----- src/interdomain/proto/slice_pb2.py | 330 ++++++++++++++++++ src/interdomain/requirements.in | 23 +- src/interdomain/service/InterdomainService.py | 68 ++-- .../service/InterdomainServiceServicerImpl.py | 101 +++--- src/interdomain/service/__main__.py | 48 ++- 11 files changed, 582 insertions(+), 295 deletions(-) rename src/interdomain/proto/{service_pb2_grpc.py => interdomain_pb2_grpc.py} (52%) create mode 100644 src/interdomain/proto/kpi_sample_types_pb2.py delete mode 100644 src/interdomain/proto/service_pb2.py create mode 100644 src/interdomain/proto/slice_pb2.py diff --git a/src/interdomain/Dockerfile b/src/interdomain/Dockerfile index 7ae62928e..35c7dbeba 100644 --- a/src/interdomain/Dockerfile +++ b/src/interdomain/Dockerfile @@ -45,9 +45,10 @@ RUN python3 -m pip install -r interdomain/requirements.txt COPY common/. common COPY context/. context COPY device/. device +COPY interdomain/. interdomain COPY monitoring/. monitoring COPY service/. service -COPY interdomain/. interdomain +COPY slice/. slice # Start interdomain service ENTRYPOINT ["python", "-m", "interdomain.service"] diff --git a/src/interdomain/client/InterdomainClient.py b/src/interdomain/client/InterdomainClient.py index be226f53a..fc61496cf 100644 --- a/src/interdomain/client/InterdomainClient.py +++ b/src/interdomain/client/InterdomainClient.py @@ -7,6 +7,7 @@ from interdomain.proto.interdomain_pb2_grpc import InterdomainServiceStub LOGGER = logging.getLogger(__name__) MAX_RETRIES = 15 DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0) +RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') class InterdomainClient: def __init__(self, address, port): @@ -26,28 +27,28 @@ class InterdomainClient: self.channel = None self.stub = None - @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') + @RETRY_DECORATOR def Authenticate(self, request : TeraFlowController) -> AuthenticationResult: LOGGER.debug('Authenticate request: {:s}'.format(str(request))) response = self.stub.Authenticate(request) LOGGER.debug('Authenticate result: {:s}'.format(str(response))) return response - @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') + @RETRY_DECORATOR def LookUpSlice(self, request : TransportSlice) -> SliceId: LOGGER.debug('LookUpSlice request: {:s}'.format(str(request))) response = self.stub.LookUpSlice(request) LOGGER.debug('LookUpSlice result: {:s}'.format(str(response))) return response - @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') + @RETRY_DECORATOR def OrderSliceFromCatalog(self, request : TransportSlice) -> SliceStatus: LOGGER.debug('OrderSliceFromCatalog request: {:s}'.format(str(request))) response = self.stub.OrderSliceFromCatalog(request) LOGGER.debug('OrderSliceFromCatalog result: {:s}'.format(str(response))) return response - @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') + @RETRY_DECORATOR def CreateSliceAndAddToCatalog(self, request : TransportSlice) -> SliceStatus: LOGGER.debug('CreateSliceAndAddToCatalog request: {:s}'.format(str(request))) response = self.stub.CreateSliceAndAddToCatalog(request) diff --git a/src/interdomain/proto/__init__.py b/src/interdomain/proto/__init__.py index 70a332512..e69de29bb 100644 --- a/src/interdomain/proto/__init__.py +++ b/src/interdomain/proto/__init__.py @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/interdomain/proto/service_pb2_grpc.py b/src/interdomain/proto/interdomain_pb2_grpc.py similarity index 52% rename from src/interdomain/proto/service_pb2_grpc.py rename to src/interdomain/proto/interdomain_pb2_grpc.py index 58cd47e93..b415f4177 100644 --- a/src/interdomain/proto/service_pb2_grpc.py +++ b/src/interdomain/proto/interdomain_pb2_grpc.py @@ -3,9 +3,10 @@ import grpc from . import context_pb2 as context__pb2 +from . import slice_pb2 as slice__pb2 -class ServiceServiceStub(object): +class InterdomainServiceStub(object): """Missing associated documentation comment in .proto file.""" def __init__(self, channel): @@ -14,90 +15,90 @@ class ServiceServiceStub(object): Args: channel: A grpc.Channel. """ - self.CreateService = channel.unary_unary( - '/service.ServiceService/CreateService', - request_serializer=context__pb2.Service.SerializeToString, - response_deserializer=context__pb2.ServiceId.FromString, + self.Authenticate = channel.unary_unary( + '/interdomain.InterdomainService/Authenticate', + request_serializer=context__pb2.TeraFlowController.SerializeToString, + response_deserializer=context__pb2.AuthenticationResult.FromString, ) - self.UpdateService = channel.unary_unary( - '/service.ServiceService/UpdateService', - request_serializer=context__pb2.Service.SerializeToString, - response_deserializer=context__pb2.ServiceId.FromString, + self.LookUpSlice = channel.unary_unary( + '/interdomain.InterdomainService/LookUpSlice', + request_serializer=slice__pb2.TransportSlice.SerializeToString, + response_deserializer=slice__pb2.SliceId.FromString, ) - self.DeleteService = channel.unary_unary( - '/service.ServiceService/DeleteService', - request_serializer=context__pb2.ServiceId.SerializeToString, - response_deserializer=context__pb2.Empty.FromString, + self.OrderSliceFromCatalog = channel.unary_unary( + '/interdomain.InterdomainService/OrderSliceFromCatalog', + request_serializer=slice__pb2.TransportSlice.SerializeToString, + response_deserializer=slice__pb2.SliceStatus.FromString, ) - self.GetConnectionList = channel.unary_unary( - '/service.ServiceService/GetConnectionList', - request_serializer=context__pb2.ServiceId.SerializeToString, - response_deserializer=context__pb2.ConnectionList.FromString, + self.CreateSliceAndAddToCatalog = channel.unary_unary( + '/interdomain.InterdomainService/CreateSliceAndAddToCatalog', + request_serializer=slice__pb2.TransportSlice.SerializeToString, + response_deserializer=slice__pb2.SliceStatus.FromString, ) -class ServiceServiceServicer(object): +class InterdomainServiceServicer(object): """Missing associated documentation comment in .proto file.""" - def CreateService(self, request, context): + def Authenticate(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 UpdateService(self, request, context): + def LookUpSlice(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 DeleteService(self, request, context): + def OrderSliceFromCatalog(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 GetConnectionList(self, request, context): + def CreateSliceAndAddToCatalog(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_ServiceServiceServicer_to_server(servicer, server): +def add_InterdomainServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'CreateService': grpc.unary_unary_rpc_method_handler( - servicer.CreateService, - request_deserializer=context__pb2.Service.FromString, - response_serializer=context__pb2.ServiceId.SerializeToString, + 'Authenticate': grpc.unary_unary_rpc_method_handler( + servicer.Authenticate, + request_deserializer=context__pb2.TeraFlowController.FromString, + response_serializer=context__pb2.AuthenticationResult.SerializeToString, ), - 'UpdateService': grpc.unary_unary_rpc_method_handler( - servicer.UpdateService, - request_deserializer=context__pb2.Service.FromString, - response_serializer=context__pb2.ServiceId.SerializeToString, + 'LookUpSlice': grpc.unary_unary_rpc_method_handler( + servicer.LookUpSlice, + request_deserializer=slice__pb2.TransportSlice.FromString, + response_serializer=slice__pb2.SliceId.SerializeToString, ), - 'DeleteService': grpc.unary_unary_rpc_method_handler( - servicer.DeleteService, - request_deserializer=context__pb2.ServiceId.FromString, - response_serializer=context__pb2.Empty.SerializeToString, + 'OrderSliceFromCatalog': grpc.unary_unary_rpc_method_handler( + servicer.OrderSliceFromCatalog, + request_deserializer=slice__pb2.TransportSlice.FromString, + response_serializer=slice__pb2.SliceStatus.SerializeToString, ), - 'GetConnectionList': grpc.unary_unary_rpc_method_handler( - servicer.GetConnectionList, - request_deserializer=context__pb2.ServiceId.FromString, - response_serializer=context__pb2.ConnectionList.SerializeToString, + 'CreateSliceAndAddToCatalog': grpc.unary_unary_rpc_method_handler( + servicer.CreateSliceAndAddToCatalog, + request_deserializer=slice__pb2.TransportSlice.FromString, + response_serializer=slice__pb2.SliceStatus.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( - 'service.ServiceService', rpc_method_handlers) + 'interdomain.InterdomainService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) # This class is part of an EXPERIMENTAL API. -class ServiceService(object): +class InterdomainService(object): """Missing associated documentation comment in .proto file.""" @staticmethod - def CreateService(request, + def Authenticate(request, target, options=(), channel_credentials=None, @@ -107,14 +108,14 @@ class ServiceService(object): wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/service.ServiceService/CreateService', - context__pb2.Service.SerializeToString, - context__pb2.ServiceId.FromString, + return grpc.experimental.unary_unary(request, target, '/interdomain.InterdomainService/Authenticate', + context__pb2.TeraFlowController.SerializeToString, + context__pb2.AuthenticationResult.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod - def UpdateService(request, + def LookUpSlice(request, target, options=(), channel_credentials=None, @@ -124,14 +125,14 @@ class ServiceService(object): wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/service.ServiceService/UpdateService', - context__pb2.Service.SerializeToString, - context__pb2.ServiceId.FromString, + return grpc.experimental.unary_unary(request, target, '/interdomain.InterdomainService/LookUpSlice', + slice__pb2.TransportSlice.SerializeToString, + slice__pb2.SliceId.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod - def DeleteService(request, + def OrderSliceFromCatalog(request, target, options=(), channel_credentials=None, @@ -141,14 +142,14 @@ class ServiceService(object): wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/service.ServiceService/DeleteService', - context__pb2.ServiceId.SerializeToString, - context__pb2.Empty.FromString, + return grpc.experimental.unary_unary(request, target, '/interdomain.InterdomainService/OrderSliceFromCatalog', + slice__pb2.TransportSlice.SerializeToString, + slice__pb2.SliceStatus.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod - def GetConnectionList(request, + def CreateSliceAndAddToCatalog(request, target, options=(), channel_credentials=None, @@ -158,8 +159,8 @@ class ServiceService(object): wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/service.ServiceService/GetConnectionList', - context__pb2.ServiceId.SerializeToString, - context__pb2.ConnectionList.FromString, + return grpc.experimental.unary_unary(request, target, '/interdomain.InterdomainService/CreateSliceAndAddToCatalog', + slice__pb2.TransportSlice.SerializeToString, + slice__pb2.SliceStatus.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/interdomain/proto/kpi_sample_types_pb2.py b/src/interdomain/proto/kpi_sample_types_pb2.py new file mode 100644 index 000000000..ea7fd2f82 --- /dev/null +++ b/src/interdomain/proto/kpi_sample_types_pb2.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: kpi_sample_types.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() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='kpi_sample_types.proto', + package='kpi_sample_types', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x16kpi_sample_types.proto\x12\x10kpi_sample_types*\xbe\x01\n\rKpiSampleType\x12\x19\n\x15KPISAMPLETYPE_UNKNOWN\x10\x00\x12%\n!KPISAMPLETYPE_PACKETS_TRANSMITTED\x10\x65\x12\"\n\x1eKPISAMPLETYPE_PACKETS_RECEIVED\x10\x66\x12$\n\x1fKPISAMPLETYPE_BYTES_TRANSMITTED\x10\xc9\x01\x12!\n\x1cKPISAMPLETYPE_BYTES_RECEIVED\x10\xca\x01\x62\x06proto3' +) + +_KPISAMPLETYPE = _descriptor.EnumDescriptor( + name='KpiSampleType', + full_name='kpi_sample_types.KpiSampleType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_PACKETS_TRANSMITTED', index=1, number=101, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_PACKETS_RECEIVED', index=2, number=102, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_BYTES_TRANSMITTED', index=3, number=201, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_BYTES_RECEIVED', index=4, number=202, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=45, + serialized_end=235, +) +_sym_db.RegisterEnumDescriptor(_KPISAMPLETYPE) + +KpiSampleType = enum_type_wrapper.EnumTypeWrapper(_KPISAMPLETYPE) +KPISAMPLETYPE_UNKNOWN = 0 +KPISAMPLETYPE_PACKETS_TRANSMITTED = 101 +KPISAMPLETYPE_PACKETS_RECEIVED = 102 +KPISAMPLETYPE_BYTES_TRANSMITTED = 201 +KPISAMPLETYPE_BYTES_RECEIVED = 202 + + +DESCRIPTOR.enum_types_by_name['KpiSampleType'] = _KPISAMPLETYPE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + +# @@protoc_insertion_point(module_scope) diff --git a/src/interdomain/proto/service_pb2.py b/src/interdomain/proto/service_pb2.py deleted file mode 100644 index 7a006915b..000000000 --- a/src/interdomain/proto/service_pb2.py +++ /dev/null @@ -1,88 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# source: service.proto -"""Generated protocol buffer code.""" -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 - - -DESCRIPTOR = _descriptor.FileDescriptor( - name='service.proto', - package='service', - syntax='proto3', - serialized_options=None, - create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xfd\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12\x42\n\x11GetConnectionList\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x62\x06proto3' - , - dependencies=[context__pb2.DESCRIPTOR,]) - - - -_sym_db.RegisterFileDescriptor(DESCRIPTOR) - - - -_SERVICESERVICE = _descriptor.ServiceDescriptor( - name='ServiceService', - full_name='service.ServiceService', - file=DESCRIPTOR, - index=0, - serialized_options=None, - create_key=_descriptor._internal_create_key, - serialized_start=42, - serialized_end=295, - methods=[ - _descriptor.MethodDescriptor( - name='CreateService', - full_name='service.ServiceService.CreateService', - index=0, - containing_service=None, - input_type=context__pb2._SERVICE, - output_type=context__pb2._SERVICEID, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), - _descriptor.MethodDescriptor( - name='UpdateService', - full_name='service.ServiceService.UpdateService', - index=1, - containing_service=None, - input_type=context__pb2._SERVICE, - output_type=context__pb2._SERVICEID, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), - _descriptor.MethodDescriptor( - name='DeleteService', - full_name='service.ServiceService.DeleteService', - index=2, - containing_service=None, - input_type=context__pb2._SERVICEID, - output_type=context__pb2._EMPTY, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), - _descriptor.MethodDescriptor( - name='GetConnectionList', - full_name='service.ServiceService.GetConnectionList', - index=3, - containing_service=None, - input_type=context__pb2._SERVICEID, - output_type=context__pb2._CONNECTIONLIST, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), -]) -_sym_db.RegisterServiceDescriptor(_SERVICESERVICE) - -DESCRIPTOR.services_by_name['ServiceService'] = _SERVICESERVICE - -# @@protoc_insertion_point(module_scope) diff --git a/src/interdomain/proto/slice_pb2.py b/src/interdomain/proto/slice_pb2.py new file mode 100644 index 000000000..91dbaaae3 --- /dev/null +++ b/src/interdomain/proto/slice_pb2.py @@ -0,0 +1,330 @@ +# -*- 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 + + +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\"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.context.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,]) + +_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=509, + serialized_end=573, +) +_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=37, + serialized_end=88, +) + + +_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=91, + serialized_end=335, +) + + +_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=337, + serialized_end=418, +) + + +_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=420, + serialized_end=507, +) + +_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 = context__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=576, + serialized_end=712, + 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) diff --git a/src/interdomain/requirements.in b/src/interdomain/requirements.in index eb922871f..58c398e7d 100644 --- a/src/interdomain/requirements.in +++ b/src/interdomain/requirements.in @@ -1,18 +1,5 @@ -anytree -apscheduler -fastcache -flask-restful -grpcio-health-checking -grpcio -Jinja2 -netconf-client #1.7.3 -prometheus-client -pytest -pytest-benchmark -python-json-logger -pytz -redis -requests -xmltodict -p4runtime==1.3.0 -coverage +grpcio==1.43.0 +grpcio-health-checking==1.43.0 +prometheus-client==0.13.0 +pytest==6.2.5 +pytest-benchmark==3.4.1 diff --git a/src/interdomain/service/InterdomainService.py b/src/interdomain/service/InterdomainService.py index 1ae1c6772..b1eaa635f 100644 --- a/src/interdomain/service/InterdomainService.py +++ b/src/interdomain/service/InterdomainService.py @@ -1,60 +1,70 @@ -from concurrent import futures - -import grpc - -from interdomain.service.InterdomainServiceServicerImpl import InterdomainServiceServicerImpl -from interdomain.Config import GRPC_SLICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD -from interdomain.proto.interdomain_pb2_grpc import add_InterdomainServiceServicer_to_server +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 grpc_health.v1 import health -from grpc_health.v1 import health_pb2 +import grpc, 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 common.logger import getJSONLogger -LOGGER = getJSONLogger('interdomainservice-server') -LOGGER.setLevel('DEBUG') +from interdomain.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD +from interdomain.proto.interdomain_pb2_grpc import add_InterdomainServiceServicer_to_server +from .InterdomainServiceServicerImpl import InterdomainServiceServicerImpl BIND_ADDRESS = '0.0.0.0' +LOGGER = logging.getLogger(__name__) class InterdomainService: - def __init__(self, address=BIND_ADDRESS, slice_client=None, port=GRPC_INTERDOMAIN_PORT, max_workers=GRPC_MAX_WORKERS, - grace_period=GRPC_GRACE_PERIOD): - self.address = address + def __init__( + self, slice_client, + address=BIND_ADDRESS, port=GRPC_SERVICE_PORT, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD): + self.slice_client = slice_client + self.address = address self.port = port self.endpoint = None self.max_workers = max_workers self.grace_period = grace_period - self.monitoring_servicer = None + self.interdomain_servicer = None self.health_servicer = None self.pool = None self.server = None def start(self): - # create gRPC server - self.server = grpc.server(futures.ThreadPoolExecutor(max_workers=self.max_workers)) # ,interceptors=(tracer_interceptor,)) + self.endpoint = '{:s}:{:s}'.format(str(self.address), str(self.port)) + LOGGER.info('Starting Service (tentative endpoint: {:s}, max_workers: {:s})...'.format( + str(self.endpoint), str(self.max_workers))) + + self.pool = futures.ThreadPoolExecutor(max_workers=self.max_workers) + self.server = grpc.server(self.pool) # , interceptors=(tracer_interceptor,)) - # add monitoring servicer class to gRPC server self.interdomain_servicer = InterdomainServiceServicerImpl() add_InterdomainServiceServicer_to_server(self.interdomain_servicer, self.server) - # add gRPC health checker servicer class to gRPC server - self.health_servicer = health.HealthServicer( + 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) - # start server - endpoint = '{}:{}'.format(self.address, self.port) - LOGGER.info('Listening on {}'.format(endpoint)) - self.server.add_insecure_port(endpoint) + port = self.server.add_insecure_port(self.endpoint) + self.endpoint = '{:s}:{:s}'.format(str(self.address), str(port)) + LOGGER.info('Listening on {:s}...'.format(str(self.endpoint))) self.server.start() - self.health_servicer.set('', health_pb2.HealthCheckResponse.SERVING) # pylint: disable=maybe-no-member + 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)) + LOGGER.debug('Stopping service (grace period {:s} seconds)...'.format(str(self.grace_period))) self.health_servicer.enter_graceful_shutdown() self.server.stop(self.grace_period) LOGGER.debug('Service stopped') - diff --git a/src/interdomain/service/InterdomainServiceServicerImpl.py b/src/interdomain/service/InterdomainServiceServicerImpl.py index 76357d876..8570651d7 100644 --- a/src/interdomain/service/InterdomainServiceServicerImpl.py +++ b/src/interdomain/service/InterdomainServiceServicerImpl.py @@ -1,66 +1,49 @@ -import os,grpc - -from interdomain.proto import interdomain_pb2 -from interdomain.proto import interdomain_pb2_grpc - -from common.rpc_method_wrapper.ServiceExceptions import ServiceException -from common.logger import getJSONLogger - -from context.proto import context_pb2 - -from slice.Config import GRPC_SERVICE_PORT -from slice.client.SliceClient import SliceClient -from slice.proto import slice_pb2 - -LOGGER = getJSONLogger('interdomainservice-server') -LOGGER.setLevel('DEBUG') - -class InterdomainServiceServicerImpl(interdomain_pb2_grpc.InterdomainServiceServicer): +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 grpc, logging +from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method +from interdomain.proto.context_pb2 import AuthenticationResult, TeraFlowController +from interdomain.proto.slice_pb2 import SliceId, SliceStatus, TransportSlice +from interdomain.proto.interdomain_pb2_grpc import InterdomainServiceServicer + +LOGGER = logging.getLogger(__name__) + +SERVICE_NAME = 'Interdomain' +METHOD_NAMES = ['Authenticate', 'LookUpSlice', 'OrderSliceFromCatalog', 'CreateSliceAndAddToCatalog'] +METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) + +class InterdomainServiceServicerImpl(InterdomainServiceServicer): def __init__(self): - LOGGER.info('Init InterdomainService') + LOGGER.debug('Creating Servicer...') + LOGGER.debug('Servicer Created') - # rpc Authenticate (context.TeraFlowController) returns (context.AuthenticationResult) {} - def Authenticate(self, request : context_pb2.TeraFlowController) -> context_pb2.AuthenticationResult : - LOGGER.info('Authenticate') - auth_result = context_pb2.AuthenticationResult() - auth_result.context_id = 0 + @safe_and_metered_rpc_method(METRICS, LOGGER) + def Authenticate(self, request : TeraFlowController, context : grpc.ServicerContext) -> AuthenticationResult: + auth_result = AuthenticationResult() + #auth_result.context_id = ... auth_result.authenticated = True return auth_result - # rpc LookUpSlice(slice.TransportSlice) returns (slice.SliceId) {} - def LookUpSlice ( self, request : slice_pb2.TransportSlice) -> slice_pb2.SliceId: - - LOGGER.info('LookUpSlice') - try: - slice_id = slice_pb2.SliceId() - - return sliceId - except Exception as e: - LOGGER.exception('LookUpSlice exception') - - - - # rpc OrderSliceFromCatalog(slice.TransportSlice) returns (slice.SliceStatus) {} - def OrderSliceFromCatalog(self, request : slice_pb2.TransportSlice) -> slice_pb2.SliceStatus: - - LOGGER.info('OrderSliceFromCatalog') - - try: - slice_status=slice_pb2.SliceStatus() - return slice_status - except Exception as e: # pragma: no cover - LOGGER.exception('OrderSliceFromCatalog exception') - - - # rpc CreateSliceAndAddToCatalog(slice.TransportSlice) returns (slice.SliceStatus) {} - def CreateSliceAndAddToCatalog(self, request : slice_pb2.TransportSlice) -> slice_pb2.SliceStatus: - - LOGGER.info('OrderSliceFromCatalog') - - try: - slice_status=slice_pb2.SliceStatus() - return slice_status - except Exception as e: # pragma: no cover - LOGGER.exception('OrderSliceFromCatalog exception') + @safe_and_metered_rpc_method(METRICS, LOGGER) + def LookUpSlice(self, request : TransportSlice, context : grpc.ServicerContext) -> SliceId: + return SliceId() + @safe_and_metered_rpc_method(METRICS, LOGGER) + def OrderSliceFromCatalog(self, request : TransportSlice, context : grpc.ServicerContext) -> SliceStatus: + return SliceStatus() + @safe_and_metered_rpc_method(METRICS, LOGGER) + def CreateSliceAndAddToCatalog(self, request : TransportSlice, context : grpc.ServicerContext) -> SliceStatus: + return SliceStatus() diff --git a/src/interdomain/service/__main__.py b/src/interdomain/service/__main__.py index 8fbe01770..2b919870a 100644 --- a/src/interdomain/service/__main__.py +++ b/src/interdomain/service/__main__.py @@ -14,15 +14,13 @@ import logging, signal, sys, threading from prometheus_client import start_http_server -from common.Settings import get_setting +from common.Settings import get_setting, wait_for_environment_variables from slice.client.SliceClient import SliceClient - from interdomain.Config import ( - SLICE_SERVICE_HOST, SLICE_SERVICE_PORT, GRPC_INTERDOMAIN_PORT, - GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, METRICS_PORT) + SLICE_SERVICE_HOST, SLICE_SERVICE_PORT, GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, + METRICS_PORT) from .InterdomainService import InterdomainService - terminate = threading.Event() LOGGER : logging.Logger = None @@ -33,22 +31,22 @@ def signal_handler(signal, frame): # pylint: disable=redefined-outer-name def main(): global LOGGER # pylint: disable=global-statement - grpc_service_port = get_setting('INTERDOMAINSERVICE_SERVICE_PORT_GRPC', default=GRPC_SERVICE_PORT ) - max_workers = get_setting('MAX_WORKERS', default=GRPC_MAX_WORKERS ) - grace_period = get_setting('GRACE_PERIOD', default=GRPC_GRACE_PERIOD ) - log_level = get_setting('LOG_LEVEL', default=LOG_LEVEL ) - metrics_port = get_setting('METRICS_PORT', default=METRICS_PORT ) + grpc_service_port = get_setting('INTERDOMAINSERVICE_SERVICE_PORT_GRPC', default=GRPC_SERVICE_PORT ) + max_workers = get_setting('MAX_WORKERS', default=GRPC_MAX_WORKERS ) + grace_period = get_setting('GRACE_PERIOD', default=GRPC_GRACE_PERIOD ) + log_level = get_setting('LOG_LEVEL', default=LOG_LEVEL ) + metrics_port = get_setting('METRICS_PORT', default=METRICS_PORT ) logging.basicConfig(level=log_level) LOGGER = logging.getLogger(__name__) - wait_for_environment_variables([ - 'CONTEXTSERVICE_SERVICE_HOST', 'CONTEXTSERVICE_SERVICE_PORT_GRPC', - 'MONITORINGSERVICE_SERVICE_HOST', 'MONITORINGSERVICE_SERVICE_PORT_GRPC' - ]) + #wait_for_environment_variables([ + # 'CONTEXTSERVICE_SERVICE_HOST', 'CONTEXTSERVICE_SERVICE_PORT_GRPC', + # 'MONITORINGSERVICE_SERVICE_HOST', 'MONITORINGSERVICE_SERVICE_PORT_GRPC' + #]) - slice_service_host = get_setting('SLICESERVICE_SERVICE_HOST', default=SLICE_SERVICE_HOST ) - slice_service_port = get_setting('SLICESERVICE_SERVICE_PORT_GRPC', default=SLICE_SERVICE_PORT ) + slice_service_host = get_setting('SLICESERVICE_SERVICE_HOST', default=SLICE_SERVICE_HOST ) + slice_service_port = get_setting('SLICESERVICE_SERVICE_PORT_GRPC', default=SLICE_SERVICE_PORT ) signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) @@ -58,23 +56,23 @@ def main(): # Start metrics server start_http_server(metrics_port) - # Initialize Slice Client - if slice_service_host is None or slice_service_port is None: - raise Exception('Wrong address({:s}):port({:s}) of Slice component'.format( - str(slice_service_host), str(slice_service_port))) - slice_client = SliceClient(slice_service_host, slice_service_port) + ## Initialize Slice Client + #if slice_service_host is None or slice_service_port is None: + # raise Exception('Wrong address({:s}):port({:s}) of Slice component'.format( + # str(slice_service_host), str(slice_service_port))) + #slice_client = SliceClient(slice_service_host, slice_service_port) # Starting Interdomain service - grpc_interdomain = InterdomainService( - slice_client=slice_client, port=grpc_interdomain_port, max_workers=max_workers, + grpc_service = InterdomainService( + slice_client, port=grpc_service_port, max_workers=max_workers, grace_period=grace_period) - grpc_interdomain.start() + grpc_service.start() # Wait for Ctrl+C or termination signal while not terminate.wait(timeout=0.1): pass LOGGER.info('Terminating...') - grpc_interdomain.stop() + grpc_service.stop() LOGGER.info('Bye') return 0 -- GitLab From afa196d752a53d8c94de86eb46aa47d8f5d2b42a Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Wed, 9 Mar 2022 20:00:06 +0100 Subject: [PATCH 010/353] Defined/Corrected skeleton for Slice component --- src/slice/client/SliceClient.py | 17 +- src/slice/proto/context_pb2.py | 2308 +++++++++++++++-- src/slice/proto/kpi_sample_types_pb2.py | 78 + src/slice/proto/service_pb2.py | 561 +--- src/slice/proto/slice_pb2.py | 31 +- src/slice/requirements.in | 11 +- src/slice/service/SliceService.py | 40 +- src/slice/service/SliceServiceServicerImpl.py | 111 +- src/slice/service/__main__.py | 80 +- 9 files changed, 2277 insertions(+), 960 deletions(-) create mode 100644 src/slice/proto/kpi_sample_types_pb2.py diff --git a/src/slice/client/SliceClient.py b/src/slice/client/SliceClient.py index b45aa3032..a056a7101 100644 --- a/src/slice/client/SliceClient.py +++ b/src/slice/client/SliceClient.py @@ -7,11 +7,12 @@ from slice.proto.slice_pb2_grpc import SliceServiceStub LOGGER = logging.getLogger(__name__) MAX_RETRIES = 15 DELAY_FUNCTION = delay_exponential(initial=0.01, increment=2.0, maximum=5.0) +RETRY_DECORATOR = retry(max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') class SliceClient: def __init__(self, address, port): - self.endpoint = '{}:{}'.format(address, port) - LOGGER.debug('Creating channel to {}...'.format(self.endpoint)) + self.endpoint = '{:s}:{:s}'.format(str(address), str(port)) + LOGGER.debug('Creating channel to {:s}...'.format(self.endpoint)) self.channel = None self.stub = None self.connect() @@ -26,16 +27,16 @@ class SliceClient: self.channel = None self.stub = None - @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') + @RETRY_DECORATOR def CreateUpdateSlice(self, request : TransportSlice) -> SliceStatus: - LOGGER.debug('CreateUpdateSlice request: {}'.format(request)) + LOGGER.debug('CreateUpdateSlice request: {:s}'.format(str(request))) response = self.stub.CreateUpdateSlice(request) - LOGGER.debug('CreateUpdateSlice result: {}'.format(response)) + LOGGER.debug('CreateUpdateSlice result: {:s}'.format(str(response))) return response - @retry(exceptions=set(), max_retries=MAX_RETRIES, delay_function=DELAY_FUNCTION, prepare_method_name='connect') + @RETRY_DECORATOR def DeleteSlice(self, request : TransportSlice) -> Empty: - LOGGER.debug('DeleteSlice request: {}'.format(request)) + LOGGER.debug('DeleteSlice request: {:s}'.format(str(request))) response = self.stub.DeleteSlice(request) - LOGGER.debug('DeleteSlice result: {}'.format(response)) + LOGGER.debug('DeleteSlice result: {:s}'.format(str(response))) return response diff --git a/src/slice/proto/context_pb2.py b/src/slice/proto/context_pb2.py index a41b1de47..68602b16f 100644 --- a/src/slice/proto/context_pb2.py +++ b/src/slice/proto/context_pb2.py @@ -12,6 +12,7 @@ from google.protobuf import symbol_database as _symbol_database _sym_db = _symbol_database.Default() +from . import kpi_sample_types_pb2 as kpi__sample__types__pb2 DESCRIPTOR = _descriptor.FileDescriptor( @@ -20,43 +21,250 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\"\x07\n\x05\x45mpty\"{\n\x07\x43ontext\x12%\n\tcontextId\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x1f\n\x04topo\x18\x02 \x01(\x0b\x32\x11.context.Topology\x12(\n\x03\x63tl\x18\x03 \x01(\x0b\x32\x1b.context.TeraFlowController\"/\n\tContextId\x12\"\n\x0b\x63ontextUuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"m\n\x08Topology\x12#\n\x06topoId\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\x12\x1f\n\x06\x64\x65vice\x18\x03 \x03(\x0b\x32\x0f.context.Device\x12\x1b\n\x04link\x18\x04 \x03(\x0b\x32\r.context.Link\"S\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12)\n\x0c\x65ndpointList\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"R\n\nTopologyId\x12%\n\tcontextId\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x1d\n\x06topoId\x18\x02 \x01(\x0b\x32\r.context.Uuid\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"\xda\x01\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12>\n\x14\x64\x65vOperationalStatus\x18\x04 \x01(\x0e\x32 .context.DeviceOperationalStatus\x12\'\n\x0c\x65ndpointList\x18\x05 \x03(\x0b\x32\x11.context.EndPoint\"%\n\x0c\x44\x65viceConfig\x12\x15\n\rdevice_config\x18\x01 \x01(\t\"C\n\x08\x45ndPoint\x12$\n\x07port_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x11\n\tport_type\x18\x02 \x01(\t\"t\n\nEndPointId\x12#\n\x06topoId\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12!\n\x06\x64\x65v_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12\x1e\n\x07port_id\x18\x03 \x01(\x0b\x32\r.context.Uuid\",\n\x08\x44\x65viceId\x12 \n\tdevice_id\x18\x01 \x01(\x0b\x32\r.context.Uuid\"(\n\x06LinkId\x12\x1e\n\x07link_id\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"K\n\x12TeraFlowController\x12\"\n\x06\x63tl_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x11\n\tipaddress\x18\x02 \x01(\t\"Q\n\x14\x41uthenticationResult\x12\"\n\x06\x63tl_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*N\n\x17\x44\x65viceOperationalStatus\x12\x0f\n\x0bKEEP_STATUS\x10\x00\x12\x15\n\x08\x44ISABLED\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x12\x0b\n\x07\x45NABLED\x10\x01\x32\xa2\x01\n\x0e\x43ontextService\x12\x32\n\x0bGetTopology\x12\x0e.context.Empty\x1a\x11.context.Topology\"\x00\x12+\n\x07\x41\x64\x64Link\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nDeleteLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + , + dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) + +_EVENTTYPEENUM = _descriptor.EnumDescriptor( + name='EventTypeEnum', + full_name='context.EventTypeEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_CREATE', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_UPDATE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_REMOVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3703, + serialized_end=3809, +) +_sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) + +EventTypeEnum = enum_type_wrapper.EnumTypeWrapper(_EVENTTYPEENUM) +_DEVICEDRIVERENUM = _descriptor.EnumDescriptor( + name='DeviceDriverEnum', + full_name='context.DeviceDriverEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_OPENCONFIG', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_TRANSPORT_API', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_P4', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_IETF_NETWORK_TOPOLOGY', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_ONF_TR_352', index=5, number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3812, + serialized_end=4009, +) +_sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) + +DeviceDriverEnum = enum_type_wrapper.EnumTypeWrapper(_DEVICEDRIVERENUM) +_DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( + name='DeviceOperationalStatusEnum', + full_name='context.DeviceOperationalStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DEVICEOPERATIONALSTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEOPERATIONALSTATUS_DISABLED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEOPERATIONALSTATUS_ENABLED', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4012, + serialized_end=4155, +) +_sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) + +DeviceOperationalStatusEnum = enum_type_wrapper.EnumTypeWrapper(_DEVICEOPERATIONALSTATUSENUM) +_SERVICETYPEENUM = _descriptor.EnumDescriptor( + name='ServiceTypeEnum', + full_name='context.ServiceTypeEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_L3NM', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_L2NM', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_TAPI_CONNECTIVITY_SERVICE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4158, + serialized_end=4287, +) +_sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) + +ServiceTypeEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICETYPEENUM) +_SERVICESTATUSENUM = _descriptor.EnumDescriptor( + name='ServiceStatusEnum', + full_name='context.ServiceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_ACTIVE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_PENDING_REMOVAL', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4290, + serialized_end=4426, ) +_sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) -_DEVICEOPERATIONALSTATUS = _descriptor.EnumDescriptor( - name='DeviceOperationalStatus', - full_name='context.DeviceOperationalStatus', +ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_CONFIGACTIONENUM = _descriptor.EnumDescriptor( + name='ConfigActionEnum', + full_name='context.ConfigActionEnum', filename=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key, values=[ _descriptor.EnumValueDescriptor( - name='KEEP_STATUS', index=0, number=0, + name='CONFIGACTION_UNDEFINED', index=0, number=0, serialized_options=None, type=None, create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( - name='DISABLED', index=1, number=-1, + name='CONFIGACTION_SET', index=1, number=1, serialized_options=None, type=None, create_key=_descriptor._internal_create_key), _descriptor.EnumValueDescriptor( - name='ENABLED', index=2, number=1, + name='CONFIGACTION_DELETE', index=2, number=2, serialized_options=None, type=None, create_key=_descriptor._internal_create_key), ], containing_type=None, serialized_options=None, - serialized_start=1271, - serialized_end=1349, + serialized_start=4428, + serialized_end=4521, ) -_sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUS) +_sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) -DeviceOperationalStatus = enum_type_wrapper.EnumTypeWrapper(_DEVICEOPERATIONALSTATUS) -KEEP_STATUS = 0 -DISABLED = -1 -ENABLED = 1 +ConfigActionEnum = enum_type_wrapper.EnumTypeWrapper(_CONFIGACTIONENUM) +EVENTTYPE_UNDEFINED = 0 +EVENTTYPE_CREATE = 1 +EVENTTYPE_UPDATE = 2 +EVENTTYPE_REMOVE = 3 +DEVICEDRIVER_UNDEFINED = 0 +DEVICEDRIVER_OPENCONFIG = 1 +DEVICEDRIVER_TRANSPORT_API = 2 +DEVICEDRIVER_P4 = 3 +DEVICEDRIVER_IETF_NETWORK_TOPOLOGY = 4 +DEVICEDRIVER_ONF_TR_352 = 5 +DEVICEOPERATIONALSTATUS_UNDEFINED = 0 +DEVICEOPERATIONALSTATUS_DISABLED = 1 +DEVICEOPERATIONALSTATUS_ENABLED = 2 +SERVICETYPE_UNKNOWN = 0 +SERVICETYPE_L3NM = 1 +SERVICETYPE_L2NM = 2 +SERVICETYPE_TAPI_CONNECTIVITY_SERVICE = 3 +SERVICESTATUS_UNDEFINED = 0 +SERVICESTATUS_PLANNED = 1 +SERVICESTATUS_ACTIVE = 2 +SERVICESTATUS_PENDING_REMOVAL = 3 +CONFIGACTION_UNDEFINED = 0 +CONFIGACTION_SET = 1 +CONFIGACTION_DELETE = 2 @@ -80,37 +288,62 @@ _EMPTY = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=26, - serialized_end=33, + serialized_start=50, + serialized_end=57, ) -_CONTEXT = _descriptor.Descriptor( - name='Context', - full_name='context.Context', +_UUID = _descriptor.Descriptor( + name='Uuid', + full_name='context.Uuid', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='contextId', full_name='context.Context.contextId', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, + name='uuid', full_name='context.Uuid.uuid', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), 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=59, + serialized_end=79, +) + + +_EVENT = _descriptor.Descriptor( + name='Event', + full_name='context.Event', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ _descriptor.FieldDescriptor( - name='topo', full_name='context.Context.topo', index=1, - number=2, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, + name='timestamp', full_name='context.Event.timestamp', index=0, + number=1, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='ctl', full_name='context.Context.ctl', index=2, - number=3, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, + name='event_type', full_name='context.Event.event_type', 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), @@ -126,8 +359,8 @@ _CONTEXT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=35, - serialized_end=158, + serialized_start=81, + serialized_end=151, ) @@ -140,7 +373,7 @@ _CONTEXTID = _descriptor.Descriptor( create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='contextUuid', full_name='context.ContextId.contextUuid', index=0, + name='context_uuid', full_name='context.ContextId.context_uuid', 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, @@ -158,40 +391,47 @@ _CONTEXTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=160, - serialized_end=207, + serialized_start=153, + serialized_end=201, ) -_TOPOLOGY = _descriptor.Descriptor( - name='Topology', - full_name='context.Topology', +_CONTEXT = _descriptor.Descriptor( + name='Context', + full_name='context.Context', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='topoId', full_name='context.Topology.topoId', index=0, - number=2, type=11, cpp_type=10, label=1, + name='context_id', full_name='context.Context.context_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='device', full_name='context.Topology.device', index=1, - number=3, type=11, cpp_type=10, label=3, + name='topology_ids', full_name='context.Context.topology_ids', 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='link', full_name='context.Topology.link', index=2, - number=4, type=11, cpp_type=10, label=3, + name='service_ids', full_name='context.Context.service_ids', 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='controller', full_name='context.Context.controller', index=3, + number=4, 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=[ ], @@ -204,29 +444,54 @@ _TOPOLOGY = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=209, - serialized_end=318, + serialized_start=204, + serialized_end=386, ) -_LINK = _descriptor.Descriptor( - name='Link', - full_name='context.Link', +_CONTEXTIDLIST = _descriptor.Descriptor( + name='ContextIdList', + full_name='context.ContextIdList', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='link_id', full_name='context.Link.link_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, + name='context_ids', full_name='context.ContextIdList.context_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=388, + serialized_end=444, +) + + +_CONTEXTLIST = _descriptor.Descriptor( + name='ContextList', + full_name='context.ContextList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ _descriptor.FieldDescriptor( - name='endpointList', full_name='context.Link.endpointList', index=1, - number=2, type=11, cpp_type=10, label=3, + name='contexts', full_name='context.ContextList.contexts', index=0, + number=1, 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, @@ -243,28 +508,28 @@ _LINK = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=320, - serialized_end=403, + serialized_start=446, + serialized_end=495, ) -_TOPOLOGYID = _descriptor.Descriptor( - name='TopologyId', - full_name='context.TopologyId', +_CONTEXTEVENT = _descriptor.Descriptor( + name='ContextEvent', + full_name='context.ContextEvent', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='contextId', full_name='context.TopologyId.contextId', index=0, + name='event', full_name='context.ContextEvent.event', 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='topoId', full_name='context.TopologyId.topoId', index=1, + name='context_id', full_name='context.ContextEvent.context_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, @@ -282,30 +547,30 @@ _TOPOLOGYID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=405, - serialized_end=487, + serialized_start=497, + serialized_end=582, ) -_CONSTRAINT = _descriptor.Descriptor( - name='Constraint', - full_name='context.Constraint', +_TOPOLOGYID = _descriptor.Descriptor( + name='TopologyId', + full_name='context.TopologyId', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='constraint_type', full_name='context.Constraint.constraint_type', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), + name='context_id', full_name='context.TopologyId.context_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='constraint_value', full_name='context.Constraint.constraint_value', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), + name='topology_uuid', full_name='context.TopologyId.topology_uuid', 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), @@ -321,50 +586,36 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=489, - serialized_end=552, + serialized_start=584, + serialized_end=674, ) -_DEVICE = _descriptor.Descriptor( - name='Device', - full_name='context.Device', +_TOPOLOGY = _descriptor.Descriptor( + name='Topology', + full_name='context.Topology', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='device_id', full_name='context.Device.device_id', index=0, + name='topology_id', full_name='context.Topology.topology_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='device_type', full_name='context.Device.device_type', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - 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='device_config', full_name='context.Device.device_config', index=2, - number=3, 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='devOperationalStatus', full_name='context.Device.devOperationalStatus', index=3, - number=4, type=14, cpp_type=8, label=1, - has_default_value=False, default_value=0, + name='device_ids', full_name='context.Topology.device_ids', 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='endpointList', full_name='context.Device.endpointList', index=4, - number=5, type=11, cpp_type=10, label=3, + name='link_ids', full_name='context.Topology.link_ids', 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, @@ -381,23 +632,23 @@ _DEVICE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=555, - serialized_end=773, + serialized_start=676, + serialized_end=802, ) -_DEVICECONFIG = _descriptor.Descriptor( - name='DeviceConfig', - full_name='context.DeviceConfig', +_TOPOLOGYIDLIST = _descriptor.Descriptor( + name='TopologyIdList', + full_name='context.TopologyIdList', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='device_config', full_name='context.DeviceConfig.device_config', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), + name='topology_ids', full_name='context.TopologyIdList.topology_ids', index=0, + number=1, 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), @@ -413,30 +664,62 @@ _DEVICECONFIG = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=775, - serialized_end=812, + serialized_start=804, + serialized_end=863, ) -_ENDPOINT = _descriptor.Descriptor( - name='EndPoint', - full_name='context.EndPoint', +_TOPOLOGYLIST = _descriptor.Descriptor( + name='TopologyList', + full_name='context.TopologyList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topologies', full_name='context.TopologyList.topologies', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=865, + serialized_end=918, +) + + +_TOPOLOGYEVENT = _descriptor.Descriptor( + name='TopologyEvent', + full_name='context.TopologyEvent', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='port_id', full_name='context.EndPoint.port_id', index=0, + name='event', full_name='context.TopologyEvent.event', 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='port_type', full_name='context.EndPoint.port_type', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), + name='topology_id', full_name='context.TopologyEvent.topology_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), @@ -452,40 +735,93 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=814, - serialized_end=881, + serialized_start=920, + serialized_end=1008, ) -_ENDPOINTID = _descriptor.Descriptor( - name='EndPointId', - full_name='context.EndPointId', +_DEVICEID = _descriptor.Descriptor( + name='DeviceId', + full_name='context.DeviceId', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='topoId', full_name='context.EndPointId.topoId', index=0, + name='device_uuid', full_name='context.DeviceId.device_uuid', 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=1010, + serialized_end=1056, +) + + +_DEVICE = _descriptor.Descriptor( + name='Device', + full_name='context.Device', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ _descriptor.FieldDescriptor( - name='dev_id', full_name='context.EndPointId.dev_id', index=1, - number=2, type=11, cpp_type=10, label=1, + name='device_id', full_name='context.Device.device_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='port_id', full_name='context.EndPointId.port_id', index=2, + name='device_type', full_name='context.Device.device_type', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='device_config', full_name='context.Device.device_config', index=2, number=3, 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='device_operational_status', full_name='context.Device.device_operational_status', index=3, + number=4, 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), + _descriptor.FieldDescriptor( + name='device_drivers', full_name='context.Device.device_drivers', index=4, + number=5, type=14, cpp_type=8, 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='device_endpoints', full_name='context.Device.device_endpoints', index=5, + number=6, 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), ], extensions=[ ], @@ -498,26 +834,129 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=883, - serialized_end=999, + serialized_start=1059, + serialized_end=1341, ) -_DEVICEID = _descriptor.Descriptor( - name='DeviceId', - full_name='context.DeviceId', +_DEVICECONFIG = _descriptor.Descriptor( + name='DeviceConfig', + full_name='context.DeviceConfig', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='config_rules', full_name='context.DeviceConfig.config_rules', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1343, + serialized_end=1400, +) + + +_DEVICEIDLIST = _descriptor.Descriptor( + name='DeviceIdList', + full_name='context.DeviceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='device_ids', full_name='context.DeviceIdList.device_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1402, + serialized_end=1455, +) + + +_DEVICELIST = _descriptor.Descriptor( + name='DeviceList', + full_name='context.DeviceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='devices', full_name='context.DeviceList.devices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1457, + serialized_end=1503, +) + + +_DEVICEEVENT = _descriptor.Descriptor( + name='DeviceEvent', + full_name='context.DeviceEvent', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='device_id', full_name='context.DeviceId.device_id', index=0, + name='event', full_name='context.DeviceEvent.event', 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='device_id', full_name='context.DeviceEvent.device_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=[ ], @@ -530,8 +969,8 @@ _DEVICEID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1001, - serialized_end=1045, + serialized_start=1505, + serialized_end=1587, ) @@ -544,7 +983,7 @@ _LINKID = _descriptor.Descriptor( create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='link_id', full_name='context.LinkId.link_id', index=0, + name='link_uuid', full_name='context.LinkId.link_uuid', 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, @@ -562,23 +1001,30 @@ _LINKID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1047, - serialized_end=1087, + serialized_start=1589, + serialized_end=1631, ) -_UUID = _descriptor.Descriptor( - name='Uuid', - full_name='context.Uuid', +_LINK = _descriptor.Descriptor( + name='Link', + full_name='context.Link', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='uuid', full_name='context.Uuid.uuid', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), + name='link_id', full_name='context.Link.link_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='link_endpoint_ids', full_name='context.Link.link_endpoint_ids', 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), @@ -594,30 +1040,55 @@ _UUID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1089, - serialized_end=1109, + serialized_start=1633, + serialized_end=1721, ) -_TERAFLOWCONTROLLER = _descriptor.Descriptor( - name='TeraFlowController', - full_name='context.TeraFlowController', +_LINKIDLIST = _descriptor.Descriptor( + name='LinkIdList', + full_name='context.LinkIdList', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='ctl_id', full_name='context.TeraFlowController.ctl_id', index=0, - number=1, type=11, cpp_type=10, label=1, - has_default_value=False, default_value=None, + name='link_ids', full_name='context.LinkIdList.link_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1723, + serialized_end=1770, +) + + +_LINKLIST = _descriptor.Descriptor( + name='LinkList', + full_name='context.LinkList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ _descriptor.FieldDescriptor( - name='ipaddress', full_name='context.TeraFlowController.ipaddress', index=1, - number=2, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), + name='links', full_name='context.LinkList.links', index=0, + number=1, 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), @@ -633,30 +1104,30 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1111, - serialized_end=1186, + serialized_start=1772, + serialized_end=1812, ) -_AUTHENTICATIONRESULT = _descriptor.Descriptor( - name='AuthenticationResult', - full_name='context.AuthenticationResult', +_LINKEVENT = _descriptor.Descriptor( + name='LinkEvent', + full_name='context.LinkEvent', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='ctl_id', full_name='context.AuthenticationResult.ctl_id', index=0, + name='event', full_name='context.LinkEvent.event', 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='authenticated', full_name='context.AuthenticationResult.authenticated', index=1, - number=2, type=8, cpp_type=7, label=1, - has_default_value=False, default_value=False, + name='link_id', full_name='context.LinkEvent.link_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), @@ -672,121 +1143,1102 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=1188, - serialized_end=1269, + serialized_start=1814, + serialized_end=1890, ) -_CONTEXT.fields_by_name['contextId'].message_type = _CONTEXTID -_CONTEXT.fields_by_name['topo'].message_type = _TOPOLOGY -_CONTEXT.fields_by_name['ctl'].message_type = _TERAFLOWCONTROLLER -_CONTEXTID.fields_by_name['contextUuid'].message_type = _UUID -_TOPOLOGY.fields_by_name['topoId'].message_type = _TOPOLOGYID -_TOPOLOGY.fields_by_name['device'].message_type = _DEVICE -_TOPOLOGY.fields_by_name['link'].message_type = _LINK -_LINK.fields_by_name['link_id'].message_type = _LINKID -_LINK.fields_by_name['endpointList'].message_type = _ENDPOINTID -_TOPOLOGYID.fields_by_name['contextId'].message_type = _CONTEXTID -_TOPOLOGYID.fields_by_name['topoId'].message_type = _UUID -_DEVICE.fields_by_name['device_id'].message_type = _DEVICEID -_DEVICE.fields_by_name['device_config'].message_type = _DEVICECONFIG -_DEVICE.fields_by_name['devOperationalStatus'].enum_type = _DEVICEOPERATIONALSTATUS -_DEVICE.fields_by_name['endpointList'].message_type = _ENDPOINT -_ENDPOINT.fields_by_name['port_id'].message_type = _ENDPOINTID -_ENDPOINTID.fields_by_name['topoId'].message_type = _TOPOLOGYID -_ENDPOINTID.fields_by_name['dev_id'].message_type = _DEVICEID -_ENDPOINTID.fields_by_name['port_id'].message_type = _UUID -_DEVICEID.fields_by_name['device_id'].message_type = _UUID -_LINKID.fields_by_name['link_id'].message_type = _UUID -_TERAFLOWCONTROLLER.fields_by_name['ctl_id'].message_type = _CONTEXTID -_AUTHENTICATIONRESULT.fields_by_name['ctl_id'].message_type = _CONTEXTID -DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY -DESCRIPTOR.message_types_by_name['Context'] = _CONTEXT -DESCRIPTOR.message_types_by_name['ContextId'] = _CONTEXTID -DESCRIPTOR.message_types_by_name['Topology'] = _TOPOLOGY -DESCRIPTOR.message_types_by_name['Link'] = _LINK -DESCRIPTOR.message_types_by_name['TopologyId'] = _TOPOLOGYID -DESCRIPTOR.message_types_by_name['Constraint'] = _CONSTRAINT -DESCRIPTOR.message_types_by_name['Device'] = _DEVICE -DESCRIPTOR.message_types_by_name['DeviceConfig'] = _DEVICECONFIG -DESCRIPTOR.message_types_by_name['EndPoint'] = _ENDPOINT -DESCRIPTOR.message_types_by_name['EndPointId'] = _ENDPOINTID -DESCRIPTOR.message_types_by_name['DeviceId'] = _DEVICEID -DESCRIPTOR.message_types_by_name['LinkId'] = _LINKID -DESCRIPTOR.message_types_by_name['Uuid'] = _UUID -DESCRIPTOR.message_types_by_name['TeraFlowController'] = _TERAFLOWCONTROLLER -DESCRIPTOR.message_types_by_name['AuthenticationResult'] = _AUTHENTICATIONRESULT -DESCRIPTOR.enum_types_by_name['DeviceOperationalStatus'] = _DEVICEOPERATIONALSTATUS -_sym_db.RegisterFileDescriptor(DESCRIPTOR) -Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), { - 'DESCRIPTOR' : _EMPTY, +_SERVICEID = _descriptor.Descriptor( + name='ServiceId', + full_name='context.ServiceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.ServiceId.context_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='service_uuid', full_name='context.ServiceId.service_uuid', 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=1892, + serialized_end=1980, +) + + +_SERVICE = _descriptor.Descriptor( + name='Service', + full_name='context.Service', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='service_id', full_name='context.Service.service_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='service_type', full_name='context.Service.service_type', 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), + _descriptor.FieldDescriptor( + name='service_endpoint_ids', full_name='context.Service.service_endpoint_ids', 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='service_constraints', full_name='context.Service.service_constraints', 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='service_status', full_name='context.Service.service_status', index=4, + number=5, 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='service_config', full_name='context.Service.service_config', 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=1983, + serialized_end=2277, +) + + +_SERVICESTATUS = _descriptor.Descriptor( + name='ServiceStatus', + full_name='context.ServiceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='service_status', full_name='context.ServiceStatus.service_status', index=0, + number=1, 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=2279, + serialized_end=2346, +) + + +_SERVICECONFIG = _descriptor.Descriptor( + name='ServiceConfig', + full_name='context.ServiceConfig', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='config_rules', full_name='context.ServiceConfig.config_rules', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2348, + serialized_end=2406, +) + + +_SERVICEIDLIST = _descriptor.Descriptor( + name='ServiceIdList', + full_name='context.ServiceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='service_ids', full_name='context.ServiceIdList.service_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2408, + serialized_end=2464, +) + + +_SERVICELIST = _descriptor.Descriptor( + name='ServiceList', + full_name='context.ServiceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='services', full_name='context.ServiceList.services', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2466, + serialized_end=2515, +) + + +_SERVICEEVENT = _descriptor.Descriptor( + name='ServiceEvent', + full_name='context.ServiceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.ServiceEvent.event', 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='service_id', full_name='context.ServiceEvent.service_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=2517, + serialized_end=2602, +) + + +_CONNECTIONID = _descriptor.Descriptor( + name='ConnectionId', + full_name='context.ConnectionId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connection_uuid', full_name='context.ConnectionId.connection_uuid', 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=2604, + serialized_end=2658, +) + + +_CONNECTION = _descriptor.Descriptor( + name='Connection', + full_name='context.Connection', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connection_id', full_name='context.Connection.connection_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='service_id', full_name='context.Connection.service_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), + _descriptor.FieldDescriptor( + name='path_hops_endpoint_ids', full_name='context.Connection.path_hops_endpoint_ids', 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='sub_service_ids', full_name='context.Connection.sub_service_ids', 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2661, + serialized_end=2857, +) + + +_CONNECTIONIDLIST = _descriptor.Descriptor( + name='ConnectionIdList', + full_name='context.ConnectionIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connection_ids', full_name='context.ConnectionIdList.connection_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2859, + serialized_end=2924, +) + + +_CONNECTIONLIST = _descriptor.Descriptor( + name='ConnectionList', + full_name='context.ConnectionList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connections', full_name='context.ConnectionList.connections', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2926, + serialized_end=2984, +) + + +_CONNECTIONEVENT = _descriptor.Descriptor( + name='ConnectionEvent', + full_name='context.ConnectionEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.ConnectionEvent.event', 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='connection_id', full_name='context.ConnectionEvent.connection_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=2986, + serialized_end=3080, +) + + +_ENDPOINTID = _descriptor.Descriptor( + name='EndPointId', + full_name='context.EndPointId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topology_id', full_name='context.EndPointId.topology_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='device_id', full_name='context.EndPointId.device_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), + _descriptor.FieldDescriptor( + name='endpoint_uuid', full_name='context.EndPointId.endpoint_uuid', index=2, + number=3, 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=3083, + serialized_end=3213, +) + + +_ENDPOINT = _descriptor.Descriptor( + name='EndPoint', + full_name='context.EndPoint', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='endpoint_id', full_name='context.EndPoint.endpoint_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='endpoint_type', full_name='context.EndPoint.endpoint_type', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='kpi_sample_types', full_name='context.EndPoint.kpi_sample_types', index=2, + number=3, type=14, cpp_type=8, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3216, + serialized_end=3350, +) + + +_CONFIGRULE = _descriptor.Descriptor( + name='ConfigRule', + full_name='context.ConfigRule', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='action', full_name='context.ConfigRule.action', index=0, + number=1, 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), + _descriptor.FieldDescriptor( + name='resource_key', full_name='context.ConfigRule.resource_key', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='resource_value', full_name='context.ConfigRule.resource_value', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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=3352, + serialized_end=3453, +) + + +_CONSTRAINT = _descriptor.Descriptor( + name='Constraint', + full_name='context.Constraint', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='constraint_type', full_name='context.Constraint.constraint_type', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='constraint_value', full_name='context.Constraint.constraint_value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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=3455, + serialized_end=3518, +) + + +_TERAFLOWCONTROLLER = _descriptor.Descriptor( + name='TeraFlowController', + full_name='context.TeraFlowController', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.TeraFlowController.context_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='ip_address', full_name='context.TeraFlowController.ip_address', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='port', full_name='context.TeraFlowController.port', index=2, + number=3, type=13, cpp_type=3, 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=3520, + serialized_end=3614, +) + + +_AUTHENTICATIONRESULT = _descriptor.Descriptor( + name='AuthenticationResult', + full_name='context.AuthenticationResult', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.AuthenticationResult.context_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='authenticated', full_name='context.AuthenticationResult.authenticated', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + 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=3616, + serialized_end=3701, +) + +_EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM +_CONTEXTID.fields_by_name['context_uuid'].message_type = _UUID +_CONTEXT.fields_by_name['context_id'].message_type = _CONTEXTID +_CONTEXT.fields_by_name['topology_ids'].message_type = _TOPOLOGYID +_CONTEXT.fields_by_name['service_ids'].message_type = _SERVICEID +_CONTEXT.fields_by_name['controller'].message_type = _TERAFLOWCONTROLLER +_CONTEXTIDLIST.fields_by_name['context_ids'].message_type = _CONTEXTID +_CONTEXTLIST.fields_by_name['contexts'].message_type = _CONTEXT +_CONTEXTEVENT.fields_by_name['event'].message_type = _EVENT +_CONTEXTEVENT.fields_by_name['context_id'].message_type = _CONTEXTID +_TOPOLOGYID.fields_by_name['context_id'].message_type = _CONTEXTID +_TOPOLOGYID.fields_by_name['topology_uuid'].message_type = _UUID +_TOPOLOGY.fields_by_name['topology_id'].message_type = _TOPOLOGYID +_TOPOLOGY.fields_by_name['device_ids'].message_type = _DEVICEID +_TOPOLOGY.fields_by_name['link_ids'].message_type = _LINKID +_TOPOLOGYIDLIST.fields_by_name['topology_ids'].message_type = _TOPOLOGYID +_TOPOLOGYLIST.fields_by_name['topologies'].message_type = _TOPOLOGY +_TOPOLOGYEVENT.fields_by_name['event'].message_type = _EVENT +_TOPOLOGYEVENT.fields_by_name['topology_id'].message_type = _TOPOLOGYID +_DEVICEID.fields_by_name['device_uuid'].message_type = _UUID +_DEVICE.fields_by_name['device_id'].message_type = _DEVICEID +_DEVICE.fields_by_name['device_config'].message_type = _DEVICECONFIG +_DEVICE.fields_by_name['device_operational_status'].enum_type = _DEVICEOPERATIONALSTATUSENUM +_DEVICE.fields_by_name['device_drivers'].enum_type = _DEVICEDRIVERENUM +_DEVICE.fields_by_name['device_endpoints'].message_type = _ENDPOINT +_DEVICECONFIG.fields_by_name['config_rules'].message_type = _CONFIGRULE +_DEVICEIDLIST.fields_by_name['device_ids'].message_type = _DEVICEID +_DEVICELIST.fields_by_name['devices'].message_type = _DEVICE +_DEVICEEVENT.fields_by_name['event'].message_type = _EVENT +_DEVICEEVENT.fields_by_name['device_id'].message_type = _DEVICEID +_LINKID.fields_by_name['link_uuid'].message_type = _UUID +_LINK.fields_by_name['link_id'].message_type = _LINKID +_LINK.fields_by_name['link_endpoint_ids'].message_type = _ENDPOINTID +_LINKIDLIST.fields_by_name['link_ids'].message_type = _LINKID +_LINKLIST.fields_by_name['links'].message_type = _LINK +_LINKEVENT.fields_by_name['event'].message_type = _EVENT +_LINKEVENT.fields_by_name['link_id'].message_type = _LINKID +_SERVICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SERVICEID.fields_by_name['service_uuid'].message_type = _UUID +_SERVICE.fields_by_name['service_id'].message_type = _SERVICEID +_SERVICE.fields_by_name['service_type'].enum_type = _SERVICETYPEENUM +_SERVICE.fields_by_name['service_endpoint_ids'].message_type = _ENDPOINTID +_SERVICE.fields_by_name['service_constraints'].message_type = _CONSTRAINT +_SERVICE.fields_by_name['service_status'].message_type = _SERVICESTATUS +_SERVICE.fields_by_name['service_config'].message_type = _SERVICECONFIG +_SERVICESTATUS.fields_by_name['service_status'].enum_type = _SERVICESTATUSENUM +_SERVICECONFIG.fields_by_name['config_rules'].message_type = _CONFIGRULE +_SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID +_SERVICELIST.fields_by_name['services'].message_type = _SERVICE +_SERVICEEVENT.fields_by_name['event'].message_type = _EVENT +_SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID +_CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID +_CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID +_CONNECTION.fields_by_name['path_hops_endpoint_ids'].message_type = _ENDPOINTID +_CONNECTION.fields_by_name['sub_service_ids'].message_type = _SERVICEID +_CONNECTIONIDLIST.fields_by_name['connection_ids'].message_type = _CONNECTIONID +_CONNECTIONLIST.fields_by_name['connections'].message_type = _CONNECTION +_CONNECTIONEVENT.fields_by_name['event'].message_type = _EVENT +_CONNECTIONEVENT.fields_by_name['connection_id'].message_type = _CONNECTIONID +_ENDPOINTID.fields_by_name['topology_id'].message_type = _TOPOLOGYID +_ENDPOINTID.fields_by_name['device_id'].message_type = _DEVICEID +_ENDPOINTID.fields_by_name['endpoint_uuid'].message_type = _UUID +_ENDPOINT.fields_by_name['endpoint_id'].message_type = _ENDPOINTID +_ENDPOINT.fields_by_name['kpi_sample_types'].enum_type = kpi__sample__types__pb2._KPISAMPLETYPE +_CONFIGRULE.fields_by_name['action'].enum_type = _CONFIGACTIONENUM +_TERAFLOWCONTROLLER.fields_by_name['context_id'].message_type = _CONTEXTID +_AUTHENTICATIONRESULT.fields_by_name['context_id'].message_type = _CONTEXTID +DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY +DESCRIPTOR.message_types_by_name['Uuid'] = _UUID +DESCRIPTOR.message_types_by_name['Event'] = _EVENT +DESCRIPTOR.message_types_by_name['ContextId'] = _CONTEXTID +DESCRIPTOR.message_types_by_name['Context'] = _CONTEXT +DESCRIPTOR.message_types_by_name['ContextIdList'] = _CONTEXTIDLIST +DESCRIPTOR.message_types_by_name['ContextList'] = _CONTEXTLIST +DESCRIPTOR.message_types_by_name['ContextEvent'] = _CONTEXTEVENT +DESCRIPTOR.message_types_by_name['TopologyId'] = _TOPOLOGYID +DESCRIPTOR.message_types_by_name['Topology'] = _TOPOLOGY +DESCRIPTOR.message_types_by_name['TopologyIdList'] = _TOPOLOGYIDLIST +DESCRIPTOR.message_types_by_name['TopologyList'] = _TOPOLOGYLIST +DESCRIPTOR.message_types_by_name['TopologyEvent'] = _TOPOLOGYEVENT +DESCRIPTOR.message_types_by_name['DeviceId'] = _DEVICEID +DESCRIPTOR.message_types_by_name['Device'] = _DEVICE +DESCRIPTOR.message_types_by_name['DeviceConfig'] = _DEVICECONFIG +DESCRIPTOR.message_types_by_name['DeviceIdList'] = _DEVICEIDLIST +DESCRIPTOR.message_types_by_name['DeviceList'] = _DEVICELIST +DESCRIPTOR.message_types_by_name['DeviceEvent'] = _DEVICEEVENT +DESCRIPTOR.message_types_by_name['LinkId'] = _LINKID +DESCRIPTOR.message_types_by_name['Link'] = _LINK +DESCRIPTOR.message_types_by_name['LinkIdList'] = _LINKIDLIST +DESCRIPTOR.message_types_by_name['LinkList'] = _LINKLIST +DESCRIPTOR.message_types_by_name['LinkEvent'] = _LINKEVENT +DESCRIPTOR.message_types_by_name['ServiceId'] = _SERVICEID +DESCRIPTOR.message_types_by_name['Service'] = _SERVICE +DESCRIPTOR.message_types_by_name['ServiceStatus'] = _SERVICESTATUS +DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG +DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST +DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST +DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID +DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION +DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST +DESCRIPTOR.message_types_by_name['ConnectionList'] = _CONNECTIONLIST +DESCRIPTOR.message_types_by_name['ConnectionEvent'] = _CONNECTIONEVENT +DESCRIPTOR.message_types_by_name['EndPointId'] = _ENDPOINTID +DESCRIPTOR.message_types_by_name['EndPoint'] = _ENDPOINT +DESCRIPTOR.message_types_by_name['ConfigRule'] = _CONFIGRULE +DESCRIPTOR.message_types_by_name['Constraint'] = _CONSTRAINT +DESCRIPTOR.message_types_by_name['TeraFlowController'] = _TERAFLOWCONTROLLER +DESCRIPTOR.message_types_by_name['AuthenticationResult'] = _AUTHENTICATIONRESULT +DESCRIPTOR.enum_types_by_name['EventTypeEnum'] = _EVENTTYPEENUM +DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM +DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM +DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM +DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), { + 'DESCRIPTOR' : _EMPTY, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Empty) + }) +_sym_db.RegisterMessage(Empty) + +Uuid = _reflection.GeneratedProtocolMessageType('Uuid', (_message.Message,), { + 'DESCRIPTOR' : _UUID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Uuid) + }) +_sym_db.RegisterMessage(Uuid) + +Event = _reflection.GeneratedProtocolMessageType('Event', (_message.Message,), { + 'DESCRIPTOR' : _EVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Event) + }) +_sym_db.RegisterMessage(Event) + +ContextId = _reflection.GeneratedProtocolMessageType('ContextId', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextId) + }) +_sym_db.RegisterMessage(ContextId) + +Context = _reflection.GeneratedProtocolMessageType('Context', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Context) + }) +_sym_db.RegisterMessage(Context) + +ContextIdList = _reflection.GeneratedProtocolMessageType('ContextIdList', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextIdList) + }) +_sym_db.RegisterMessage(ContextIdList) + +ContextList = _reflection.GeneratedProtocolMessageType('ContextList', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextList) + }) +_sym_db.RegisterMessage(ContextList) + +ContextEvent = _reflection.GeneratedProtocolMessageType('ContextEvent', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextEvent) + }) +_sym_db.RegisterMessage(ContextEvent) + +TopologyId = _reflection.GeneratedProtocolMessageType('TopologyId', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyId) + }) +_sym_db.RegisterMessage(TopologyId) + +Topology = _reflection.GeneratedProtocolMessageType('Topology', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGY, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Topology) + }) +_sym_db.RegisterMessage(Topology) + +TopologyIdList = _reflection.GeneratedProtocolMessageType('TopologyIdList', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyIdList) + }) +_sym_db.RegisterMessage(TopologyIdList) + +TopologyList = _reflection.GeneratedProtocolMessageType('TopologyList', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyList) + }) +_sym_db.RegisterMessage(TopologyList) + +TopologyEvent = _reflection.GeneratedProtocolMessageType('TopologyEvent', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyEvent) + }) +_sym_db.RegisterMessage(TopologyEvent) + +DeviceId = _reflection.GeneratedProtocolMessageType('DeviceId', (_message.Message,), { + 'DESCRIPTOR' : _DEVICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceId) + }) +_sym_db.RegisterMessage(DeviceId) + +Device = _reflection.GeneratedProtocolMessageType('Device', (_message.Message,), { + 'DESCRIPTOR' : _DEVICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Device) + }) +_sym_db.RegisterMessage(Device) + +DeviceConfig = _reflection.GeneratedProtocolMessageType('DeviceConfig', (_message.Message,), { + 'DESCRIPTOR' : _DEVICECONFIG, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceConfig) + }) +_sym_db.RegisterMessage(DeviceConfig) + +DeviceIdList = _reflection.GeneratedProtocolMessageType('DeviceIdList', (_message.Message,), { + 'DESCRIPTOR' : _DEVICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceIdList) + }) +_sym_db.RegisterMessage(DeviceIdList) + +DeviceList = _reflection.GeneratedProtocolMessageType('DeviceList', (_message.Message,), { + 'DESCRIPTOR' : _DEVICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceList) + }) +_sym_db.RegisterMessage(DeviceList) + +DeviceEvent = _reflection.GeneratedProtocolMessageType('DeviceEvent', (_message.Message,), { + 'DESCRIPTOR' : _DEVICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceEvent) + }) +_sym_db.RegisterMessage(DeviceEvent) + +LinkId = _reflection.GeneratedProtocolMessageType('LinkId', (_message.Message,), { + 'DESCRIPTOR' : _LINKID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkId) + }) +_sym_db.RegisterMessage(LinkId) + +Link = _reflection.GeneratedProtocolMessageType('Link', (_message.Message,), { + 'DESCRIPTOR' : _LINK, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Link) + }) +_sym_db.RegisterMessage(Link) + +LinkIdList = _reflection.GeneratedProtocolMessageType('LinkIdList', (_message.Message,), { + 'DESCRIPTOR' : _LINKIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkIdList) + }) +_sym_db.RegisterMessage(LinkIdList) + +LinkList = _reflection.GeneratedProtocolMessageType('LinkList', (_message.Message,), { + 'DESCRIPTOR' : _LINKLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkList) + }) +_sym_db.RegisterMessage(LinkList) + +LinkEvent = _reflection.GeneratedProtocolMessageType('LinkEvent', (_message.Message,), { + 'DESCRIPTOR' : _LINKEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkEvent) + }) +_sym_db.RegisterMessage(LinkEvent) + +ServiceId = _reflection.GeneratedProtocolMessageType('ServiceId', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEID, '__module__' : 'context_pb2' - # @@protoc_insertion_point(class_scope:context.Empty) + # @@protoc_insertion_point(class_scope:context.ServiceId) }) -_sym_db.RegisterMessage(Empty) +_sym_db.RegisterMessage(ServiceId) -Context = _reflection.GeneratedProtocolMessageType('Context', (_message.Message,), { - 'DESCRIPTOR' : _CONTEXT, +Service = _reflection.GeneratedProtocolMessageType('Service', (_message.Message,), { + 'DESCRIPTOR' : _SERVICE, '__module__' : 'context_pb2' - # @@protoc_insertion_point(class_scope:context.Context) + # @@protoc_insertion_point(class_scope:context.Service) }) -_sym_db.RegisterMessage(Context) +_sym_db.RegisterMessage(Service) -ContextId = _reflection.GeneratedProtocolMessageType('ContextId', (_message.Message,), { - 'DESCRIPTOR' : _CONTEXTID, +ServiceStatus = _reflection.GeneratedProtocolMessageType('ServiceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SERVICESTATUS, '__module__' : 'context_pb2' - # @@protoc_insertion_point(class_scope:context.ContextId) + # @@protoc_insertion_point(class_scope:context.ServiceStatus) }) -_sym_db.RegisterMessage(ContextId) +_sym_db.RegisterMessage(ServiceStatus) -Topology = _reflection.GeneratedProtocolMessageType('Topology', (_message.Message,), { - 'DESCRIPTOR' : _TOPOLOGY, +ServiceConfig = _reflection.GeneratedProtocolMessageType('ServiceConfig', (_message.Message,), { + 'DESCRIPTOR' : _SERVICECONFIG, '__module__' : 'context_pb2' - # @@protoc_insertion_point(class_scope:context.Topology) + # @@protoc_insertion_point(class_scope:context.ServiceConfig) }) -_sym_db.RegisterMessage(Topology) +_sym_db.RegisterMessage(ServiceConfig) -Link = _reflection.GeneratedProtocolMessageType('Link', (_message.Message,), { - 'DESCRIPTOR' : _LINK, +ServiceIdList = _reflection.GeneratedProtocolMessageType('ServiceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEIDLIST, '__module__' : 'context_pb2' - # @@protoc_insertion_point(class_scope:context.Link) + # @@protoc_insertion_point(class_scope:context.ServiceIdList) }) -_sym_db.RegisterMessage(Link) +_sym_db.RegisterMessage(ServiceIdList) -TopologyId = _reflection.GeneratedProtocolMessageType('TopologyId', (_message.Message,), { - 'DESCRIPTOR' : _TOPOLOGYID, +ServiceList = _reflection.GeneratedProtocolMessageType('ServiceList', (_message.Message,), { + 'DESCRIPTOR' : _SERVICELIST, '__module__' : 'context_pb2' - # @@protoc_insertion_point(class_scope:context.TopologyId) + # @@protoc_insertion_point(class_scope:context.ServiceList) }) -_sym_db.RegisterMessage(TopologyId) +_sym_db.RegisterMessage(ServiceList) -Constraint = _reflection.GeneratedProtocolMessageType('Constraint', (_message.Message,), { - 'DESCRIPTOR' : _CONSTRAINT, +ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEEVENT, '__module__' : 'context_pb2' - # @@protoc_insertion_point(class_scope:context.Constraint) + # @@protoc_insertion_point(class_scope:context.ServiceEvent) }) -_sym_db.RegisterMessage(Constraint) +_sym_db.RegisterMessage(ServiceEvent) -Device = _reflection.GeneratedProtocolMessageType('Device', (_message.Message,), { - 'DESCRIPTOR' : _DEVICE, +ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONID, '__module__' : 'context_pb2' - # @@protoc_insertion_point(class_scope:context.Device) + # @@protoc_insertion_point(class_scope:context.ConnectionId) }) -_sym_db.RegisterMessage(Device) +_sym_db.RegisterMessage(ConnectionId) -DeviceConfig = _reflection.GeneratedProtocolMessageType('DeviceConfig', (_message.Message,), { - 'DESCRIPTOR' : _DEVICECONFIG, +Connection = _reflection.GeneratedProtocolMessageType('Connection', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTION, '__module__' : 'context_pb2' - # @@protoc_insertion_point(class_scope:context.DeviceConfig) + # @@protoc_insertion_point(class_scope:context.Connection) }) -_sym_db.RegisterMessage(DeviceConfig) +_sym_db.RegisterMessage(Connection) -EndPoint = _reflection.GeneratedProtocolMessageType('EndPoint', (_message.Message,), { - 'DESCRIPTOR' : _ENDPOINT, +ConnectionIdList = _reflection.GeneratedProtocolMessageType('ConnectionIdList', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONIDLIST, '__module__' : 'context_pb2' - # @@protoc_insertion_point(class_scope:context.EndPoint) + # @@protoc_insertion_point(class_scope:context.ConnectionIdList) }) -_sym_db.RegisterMessage(EndPoint) +_sym_db.RegisterMessage(ConnectionIdList) + +ConnectionList = _reflection.GeneratedProtocolMessageType('ConnectionList', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionList) + }) +_sym_db.RegisterMessage(ConnectionList) + +ConnectionEvent = _reflection.GeneratedProtocolMessageType('ConnectionEvent', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionEvent) + }) +_sym_db.RegisterMessage(ConnectionEvent) EndPointId = _reflection.GeneratedProtocolMessageType('EndPointId', (_message.Message,), { 'DESCRIPTOR' : _ENDPOINTID, @@ -795,26 +2247,26 @@ EndPointId = _reflection.GeneratedProtocolMessageType('EndPointId', (_message.Me }) _sym_db.RegisterMessage(EndPointId) -DeviceId = _reflection.GeneratedProtocolMessageType('DeviceId', (_message.Message,), { - 'DESCRIPTOR' : _DEVICEID, +EndPoint = _reflection.GeneratedProtocolMessageType('EndPoint', (_message.Message,), { + 'DESCRIPTOR' : _ENDPOINT, '__module__' : 'context_pb2' - # @@protoc_insertion_point(class_scope:context.DeviceId) + # @@protoc_insertion_point(class_scope:context.EndPoint) }) -_sym_db.RegisterMessage(DeviceId) +_sym_db.RegisterMessage(EndPoint) -LinkId = _reflection.GeneratedProtocolMessageType('LinkId', (_message.Message,), { - 'DESCRIPTOR' : _LINKID, +ConfigRule = _reflection.GeneratedProtocolMessageType('ConfigRule', (_message.Message,), { + 'DESCRIPTOR' : _CONFIGRULE, '__module__' : 'context_pb2' - # @@protoc_insertion_point(class_scope:context.LinkId) + # @@protoc_insertion_point(class_scope:context.ConfigRule) }) -_sym_db.RegisterMessage(LinkId) +_sym_db.RegisterMessage(ConfigRule) -Uuid = _reflection.GeneratedProtocolMessageType('Uuid', (_message.Message,), { - 'DESCRIPTOR' : _UUID, +Constraint = _reflection.GeneratedProtocolMessageType('Constraint', (_message.Message,), { + 'DESCRIPTOR' : _CONSTRAINT, '__module__' : 'context_pb2' - # @@protoc_insertion_point(class_scope:context.Uuid) + # @@protoc_insertion_point(class_scope:context.Constraint) }) -_sym_db.RegisterMessage(Uuid) +_sym_db.RegisterMessage(Constraint) TeraFlowController = _reflection.GeneratedProtocolMessageType('TeraFlowController', (_message.Message,), { 'DESCRIPTOR' : _TERAFLOWCONTROLLER, @@ -839,39 +2291,369 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=1352, - serialized_end=1514, + serialized_start=4524, + serialized_end=6617, methods=[ _descriptor.MethodDescriptor( - name='GetTopology', - full_name='context.ContextService.GetTopology', + name='ListContextIds', + full_name='context.ContextService.ListContextIds', index=0, containing_service=None, input_type=_EMPTY, - output_type=_TOPOLOGY, + output_type=_CONTEXTIDLIST, serialized_options=None, create_key=_descriptor._internal_create_key, ), _descriptor.MethodDescriptor( - name='AddLink', - full_name='context.ContextService.AddLink', + name='ListContexts', + full_name='context.ContextService.ListContexts', index=1, containing_service=None, + input_type=_EMPTY, + output_type=_CONTEXTLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetContext', + full_name='context.ContextService.GetContext', + index=2, + containing_service=None, + input_type=_CONTEXTID, + output_type=_CONTEXT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetContext', + full_name='context.ContextService.SetContext', + index=3, + containing_service=None, + input_type=_CONTEXT, + output_type=_CONTEXTID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveContext', + full_name='context.ContextService.RemoveContext', + index=4, + containing_service=None, + input_type=_CONTEXTID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetContextEvents', + full_name='context.ContextService.GetContextEvents', + index=5, + containing_service=None, + input_type=_EMPTY, + output_type=_CONTEXTEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListTopologyIds', + full_name='context.ContextService.ListTopologyIds', + index=6, + containing_service=None, + input_type=_CONTEXTID, + output_type=_TOPOLOGYIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListTopologies', + full_name='context.ContextService.ListTopologies', + index=7, + containing_service=None, + input_type=_CONTEXTID, + output_type=_TOPOLOGYLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetTopology', + full_name='context.ContextService.GetTopology', + index=8, + containing_service=None, + input_type=_TOPOLOGYID, + output_type=_TOPOLOGY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetTopology', + full_name='context.ContextService.SetTopology', + index=9, + containing_service=None, + input_type=_TOPOLOGY, + output_type=_TOPOLOGYID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveTopology', + full_name='context.ContextService.RemoveTopology', + index=10, + containing_service=None, + input_type=_TOPOLOGYID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetTopologyEvents', + full_name='context.ContextService.GetTopologyEvents', + index=11, + containing_service=None, + input_type=_EMPTY, + output_type=_TOPOLOGYEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListDeviceIds', + full_name='context.ContextService.ListDeviceIds', + index=12, + containing_service=None, + input_type=_EMPTY, + output_type=_DEVICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListDevices', + full_name='context.ContextService.ListDevices', + index=13, + containing_service=None, + input_type=_EMPTY, + output_type=_DEVICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetDevice', + full_name='context.ContextService.GetDevice', + index=14, + containing_service=None, + input_type=_DEVICEID, + output_type=_DEVICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetDevice', + full_name='context.ContextService.SetDevice', + index=15, + containing_service=None, + input_type=_DEVICE, + output_type=_DEVICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveDevice', + full_name='context.ContextService.RemoveDevice', + index=16, + containing_service=None, + input_type=_DEVICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetDeviceEvents', + full_name='context.ContextService.GetDeviceEvents', + index=17, + containing_service=None, + input_type=_EMPTY, + output_type=_DEVICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListLinkIds', + full_name='context.ContextService.ListLinkIds', + index=18, + containing_service=None, + input_type=_EMPTY, + output_type=_LINKIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListLinks', + full_name='context.ContextService.ListLinks', + index=19, + containing_service=None, + input_type=_EMPTY, + output_type=_LINKLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetLink', + full_name='context.ContextService.GetLink', + index=20, + containing_service=None, + input_type=_LINKID, + output_type=_LINK, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetLink', + full_name='context.ContextService.SetLink', + index=21, + containing_service=None, input_type=_LINK, output_type=_LINKID, serialized_options=None, create_key=_descriptor._internal_create_key, ), _descriptor.MethodDescriptor( - name='DeleteLink', - full_name='context.ContextService.DeleteLink', - index=2, + name='RemoveLink', + full_name='context.ContextService.RemoveLink', + index=22, containing_service=None, input_type=_LINKID, output_type=_EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='GetLinkEvents', + full_name='context.ContextService.GetLinkEvents', + index=23, + containing_service=None, + input_type=_EMPTY, + output_type=_LINKEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListServiceIds', + full_name='context.ContextService.ListServiceIds', + index=24, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SERVICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListServices', + full_name='context.ContextService.ListServices', + index=25, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SERVICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetService', + full_name='context.ContextService.GetService', + index=26, + containing_service=None, + input_type=_SERVICEID, + output_type=_SERVICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetService', + full_name='context.ContextService.SetService', + index=27, + containing_service=None, + input_type=_SERVICE, + output_type=_SERVICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveService', + full_name='context.ContextService.RemoveService', + index=28, + containing_service=None, + input_type=_SERVICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetServiceEvents', + full_name='context.ContextService.GetServiceEvents', + index=29, + containing_service=None, + input_type=_EMPTY, + output_type=_SERVICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListConnectionIds', + full_name='context.ContextService.ListConnectionIds', + index=30, + containing_service=None, + input_type=_SERVICEID, + output_type=_CONNECTIONIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListConnections', + full_name='context.ContextService.ListConnections', + index=31, + containing_service=None, + input_type=_SERVICEID, + output_type=_CONNECTIONLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetConnection', + full_name='context.ContextService.GetConnection', + index=32, + containing_service=None, + input_type=_CONNECTIONID, + output_type=_CONNECTION, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetConnection', + full_name='context.ContextService.SetConnection', + index=33, + containing_service=None, + input_type=_CONNECTION, + output_type=_CONNECTIONID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveConnection', + full_name='context.ContextService.RemoveConnection', + index=34, + containing_service=None, + input_type=_CONNECTIONID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetConnectionEvents', + full_name='context.ContextService.GetConnectionEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_CONNECTIONEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), ]) _sym_db.RegisterServiceDescriptor(_CONTEXTSERVICE) diff --git a/src/slice/proto/kpi_sample_types_pb2.py b/src/slice/proto/kpi_sample_types_pb2.py new file mode 100644 index 000000000..ea7fd2f82 --- /dev/null +++ b/src/slice/proto/kpi_sample_types_pb2.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: kpi_sample_types.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() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='kpi_sample_types.proto', + package='kpi_sample_types', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x16kpi_sample_types.proto\x12\x10kpi_sample_types*\xbe\x01\n\rKpiSampleType\x12\x19\n\x15KPISAMPLETYPE_UNKNOWN\x10\x00\x12%\n!KPISAMPLETYPE_PACKETS_TRANSMITTED\x10\x65\x12\"\n\x1eKPISAMPLETYPE_PACKETS_RECEIVED\x10\x66\x12$\n\x1fKPISAMPLETYPE_BYTES_TRANSMITTED\x10\xc9\x01\x12!\n\x1cKPISAMPLETYPE_BYTES_RECEIVED\x10\xca\x01\x62\x06proto3' +) + +_KPISAMPLETYPE = _descriptor.EnumDescriptor( + name='KpiSampleType', + full_name='kpi_sample_types.KpiSampleType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_PACKETS_TRANSMITTED', index=1, number=101, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_PACKETS_RECEIVED', index=2, number=102, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_BYTES_TRANSMITTED', index=3, number=201, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_BYTES_RECEIVED', index=4, number=202, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=45, + serialized_end=235, +) +_sym_db.RegisterEnumDescriptor(_KPISAMPLETYPE) + +KpiSampleType = enum_type_wrapper.EnumTypeWrapper(_KPISAMPLETYPE) +KPISAMPLETYPE_UNKNOWN = 0 +KPISAMPLETYPE_PACKETS_TRANSMITTED = 101 +KPISAMPLETYPE_PACKETS_RECEIVED = 102 +KPISAMPLETYPE_BYTES_TRANSMITTED = 201 +KPISAMPLETYPE_BYTES_RECEIVED = 202 + + +DESCRIPTOR.enum_types_by_name['KpiSampleType'] = _KPISAMPLETYPE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + +# @@protoc_insertion_point(module_scope) diff --git a/src/slice/proto/service_pb2.py b/src/slice/proto/service_pb2.py index ed248a038..8e2806c76 100644 --- a/src/slice/proto/service_pb2.py +++ b/src/slice/proto/service_pb2.py @@ -2,7 +2,6 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # source: service.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 @@ -21,522 +20,14 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto\"+\n\x0bServiceList\x12\x1c\n\x02\x63s\x18\x01 \x03(\x0b\x32\x10.service.Service\"\x87\x02\n\x07Service\x12!\n\x05\x63s_id\x18\x01 \x01(\x0b\x32\x12.service.ServiceId\x12)\n\x0bserviceType\x18\x02 \x01(\x0e\x32\x14.service.ServiceType\x12)\n\x0c\x65ndpointList\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\'\n\nconstraint\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12+\n\x0cserviceState\x18\x05 \x01(\x0b\x32\x15.service.ServiceState\x12-\n\rserviceConfig\x18\x06 \x01(\x0b\x32\x16.service.ServiceConfig\"&\n\rServiceConfig\x12\x15\n\rserviceConfig\x18\x01 \x01(\t\"P\n\tServiceId\x12%\n\tcontextId\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x1c\n\x05\x63s_id\x18\x02 \x01(\x0b\x32\r.context.Uuid\":\n\rServiceIdList\x12)\n\rserviceIdList\x18\x01 \x03(\x0b\x32\x12.service.ServiceId\"?\n\x0cServiceState\x12/\n\x0cserviceState\x18\x01 \x01(\x0e\x32\x19.service.ServiceStateEnum\"=\n\x0e\x43onnectionList\x12+\n\x0e\x63onnectionList\x18\x01 \x03(\x0b\x32\x13.service.Connection\"\x84\x01\n\nConnection\x12%\n\x06\x63on_id\x18\x01 \x01(\x0b\x32\x15.service.ConnectionId\x12,\n\x10relatedServiceId\x18\x02 \x01(\x0b\x32\x12.service.ServiceId\x12!\n\x04path\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\"-\n\x0c\x43onnectionId\x12\x1d\n\x06\x63on_id\x18\x01 \x01(\x0b\x32\r.context.Uuid*M\n\x0bServiceType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x08\n\x04L3NM\x10\x01\x12\x08\n\x04L2NM\x10\x02\x12\x1d\n\x19TAPI_CONNECTIVITY_SERVICE\x10\x03*@\n\x10ServiceStateEnum\x12\x0b\n\x07PLANNED\x10\x00\x12\n\n\x06\x41\x43TIVE\x10\x01\x12\x13\n\x0fPENDING_REMOVAL\x10\x02\x32\xed\x02\n\x0eServiceService\x12\x38\n\x0eGetServiceList\x12\x0e.context.Empty\x1a\x14.service.ServiceList\"\x00\x12\x37\n\rCreateService\x12\x10.service.Service\x1a\x12.service.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.service.Service\x1a\x12.service.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.service.ServiceId\x1a\x0e.context.Empty\"\x00\x12\x38\n\x0eGetServiceById\x12\x12.service.ServiceId\x1a\x10.service.Service\"\x00\x12>\n\x11GetConnectionList\x12\x0e.context.Empty\x1a\x17.service.ConnectionList\"\x00\x62\x06proto3' + serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xb9\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x62\x06proto3' , dependencies=[context__pb2.DESCRIPTOR,]) -_SERVICETYPE = _descriptor.EnumDescriptor( - name='ServiceType', - full_name='service.ServiceType', - filename=None, - file=DESCRIPTOR, - create_key=_descriptor._internal_create_key, - values=[ - _descriptor.EnumValueDescriptor( - name='UNKNOWN', index=0, number=0, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='L3NM', index=1, number=1, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='L2NM', index=2, number=2, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='TAPI_CONNECTIVITY_SERVICE', index=3, number=3, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - ], - containing_type=None, - serialized_options=None, - serialized_start=844, - serialized_end=921, -) -_sym_db.RegisterEnumDescriptor(_SERVICETYPE) - -ServiceType = enum_type_wrapper.EnumTypeWrapper(_SERVICETYPE) -_SERVICESTATEENUM = _descriptor.EnumDescriptor( - name='ServiceStateEnum', - full_name='service.ServiceStateEnum', - 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='ACTIVE', index=1, number=1, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - _descriptor.EnumValueDescriptor( - name='PENDING_REMOVAL', index=2, number=2, - serialized_options=None, - type=None, - create_key=_descriptor._internal_create_key), - ], - containing_type=None, - serialized_options=None, - serialized_start=923, - serialized_end=987, -) -_sym_db.RegisterEnumDescriptor(_SERVICESTATEENUM) - -ServiceStateEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATEENUM) -UNKNOWN = 0 -L3NM = 1 -L2NM = 2 -TAPI_CONNECTIVITY_SERVICE = 3 -PLANNED = 0 -ACTIVE = 1 -PENDING_REMOVAL = 2 - - - -_SERVICELIST = _descriptor.Descriptor( - name='ServiceList', - full_name='service.ServiceList', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='cs', full_name='service.ServiceList.cs', index=0, - number=1, 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), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=41, - serialized_end=84, -) - - -_SERVICE = _descriptor.Descriptor( - name='Service', - full_name='service.Service', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='cs_id', full_name='service.Service.cs_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='serviceType', full_name='service.Service.serviceType', 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), - _descriptor.FieldDescriptor( - name='endpointList', full_name='service.Service.endpointList', 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='constraint', full_name='service.Service.constraint', 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='serviceState', full_name='service.Service.serviceState', index=4, - number=5, 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='serviceConfig', full_name='service.Service.serviceConfig', 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=87, - serialized_end=350, -) - - -_SERVICECONFIG = _descriptor.Descriptor( - name='ServiceConfig', - full_name='service.ServiceConfig', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='serviceConfig', full_name='service.ServiceConfig.serviceConfig', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - 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=390, -) - - -_SERVICEID = _descriptor.Descriptor( - name='ServiceId', - full_name='service.ServiceId', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='contextId', full_name='service.ServiceId.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='cs_id', full_name='service.ServiceId.cs_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=392, - serialized_end=472, -) - - -_SERVICEIDLIST = _descriptor.Descriptor( - name='ServiceIdList', - full_name='service.ServiceIdList', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='serviceIdList', full_name='service.ServiceIdList.serviceIdList', index=0, - number=1, 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), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=474, - serialized_end=532, -) - - -_SERVICESTATE = _descriptor.Descriptor( - name='ServiceState', - full_name='service.ServiceState', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='serviceState', full_name='service.ServiceState.serviceState', index=0, - number=1, 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=534, - serialized_end=597, -) - - -_CONNECTIONLIST = _descriptor.Descriptor( - name='ConnectionList', - full_name='service.ConnectionList', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='connectionList', full_name='service.ConnectionList.connectionList', index=0, - number=1, 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), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=599, - serialized_end=660, -) - - -_CONNECTION = _descriptor.Descriptor( - name='Connection', - full_name='service.Connection', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='con_id', full_name='service.Connection.con_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='relatedServiceId', full_name='service.Connection.relatedServiceId', 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), - _descriptor.FieldDescriptor( - name='path', full_name='service.Connection.path', 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), - ], - extensions=[ - ], - nested_types=[], - enum_types=[ - ], - serialized_options=None, - is_extendable=False, - syntax='proto3', - extension_ranges=[], - oneofs=[ - ], - serialized_start=663, - serialized_end=795, -) -_CONNECTIONID = _descriptor.Descriptor( - name='ConnectionId', - full_name='service.ConnectionId', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='con_id', full_name='service.ConnectionId.con_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=797, - serialized_end=842, -) - -_SERVICELIST.fields_by_name['cs'].message_type = _SERVICE -_SERVICE.fields_by_name['cs_id'].message_type = _SERVICEID -_SERVICE.fields_by_name['serviceType'].enum_type = _SERVICETYPE -_SERVICE.fields_by_name['endpointList'].message_type = context__pb2._ENDPOINTID -_SERVICE.fields_by_name['constraint'].message_type = context__pb2._CONSTRAINT -_SERVICE.fields_by_name['serviceState'].message_type = _SERVICESTATE -_SERVICE.fields_by_name['serviceConfig'].message_type = _SERVICECONFIG -_SERVICEID.fields_by_name['contextId'].message_type = context__pb2._CONTEXTID -_SERVICEID.fields_by_name['cs_id'].message_type = context__pb2._UUID -_SERVICEIDLIST.fields_by_name['serviceIdList'].message_type = _SERVICEID -_SERVICESTATE.fields_by_name['serviceState'].enum_type = _SERVICESTATEENUM -_CONNECTIONLIST.fields_by_name['connectionList'].message_type = _CONNECTION -_CONNECTION.fields_by_name['con_id'].message_type = _CONNECTIONID -_CONNECTION.fields_by_name['relatedServiceId'].message_type = _SERVICEID -_CONNECTION.fields_by_name['path'].message_type = context__pb2._ENDPOINTID -_CONNECTIONID.fields_by_name['con_id'].message_type = context__pb2._UUID -DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST -DESCRIPTOR.message_types_by_name['Service'] = _SERVICE -DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG -DESCRIPTOR.message_types_by_name['ServiceId'] = _SERVICEID -DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST -DESCRIPTOR.message_types_by_name['ServiceState'] = _SERVICESTATE -DESCRIPTOR.message_types_by_name['ConnectionList'] = _CONNECTIONLIST -DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION -DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID -DESCRIPTOR.enum_types_by_name['ServiceType'] = _SERVICETYPE -DESCRIPTOR.enum_types_by_name['ServiceStateEnum'] = _SERVICESTATEENUM _sym_db.RegisterFileDescriptor(DESCRIPTOR) -ServiceList = _reflection.GeneratedProtocolMessageType('ServiceList', (_message.Message,), { - 'DESCRIPTOR' : _SERVICELIST, - '__module__' : 'service_pb2' - # @@protoc_insertion_point(class_scope:service.ServiceList) - }) -_sym_db.RegisterMessage(ServiceList) - -Service = _reflection.GeneratedProtocolMessageType('Service', (_message.Message,), { - 'DESCRIPTOR' : _SERVICE, - '__module__' : 'service_pb2' - # @@protoc_insertion_point(class_scope:service.Service) - }) -_sym_db.RegisterMessage(Service) - -ServiceConfig = _reflection.GeneratedProtocolMessageType('ServiceConfig', (_message.Message,), { - 'DESCRIPTOR' : _SERVICECONFIG, - '__module__' : 'service_pb2' - # @@protoc_insertion_point(class_scope:service.ServiceConfig) - }) -_sym_db.RegisterMessage(ServiceConfig) - -ServiceId = _reflection.GeneratedProtocolMessageType('ServiceId', (_message.Message,), { - 'DESCRIPTOR' : _SERVICEID, - '__module__' : 'service_pb2' - # @@protoc_insertion_point(class_scope:service.ServiceId) - }) -_sym_db.RegisterMessage(ServiceId) - -ServiceIdList = _reflection.GeneratedProtocolMessageType('ServiceIdList', (_message.Message,), { - 'DESCRIPTOR' : _SERVICEIDLIST, - '__module__' : 'service_pb2' - # @@protoc_insertion_point(class_scope:service.ServiceIdList) - }) -_sym_db.RegisterMessage(ServiceIdList) - -ServiceState = _reflection.GeneratedProtocolMessageType('ServiceState', (_message.Message,), { - 'DESCRIPTOR' : _SERVICESTATE, - '__module__' : 'service_pb2' - # @@protoc_insertion_point(class_scope:service.ServiceState) - }) -_sym_db.RegisterMessage(ServiceState) - -ConnectionList = _reflection.GeneratedProtocolMessageType('ConnectionList', (_message.Message,), { - 'DESCRIPTOR' : _CONNECTIONLIST, - '__module__' : 'service_pb2' - # @@protoc_insertion_point(class_scope:service.ConnectionList) - }) -_sym_db.RegisterMessage(ConnectionList) - -Connection = _reflection.GeneratedProtocolMessageType('Connection', (_message.Message,), { - 'DESCRIPTOR' : _CONNECTION, - '__module__' : 'service_pb2' - # @@protoc_insertion_point(class_scope:service.Connection) - }) -_sym_db.RegisterMessage(Connection) - -ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { - 'DESCRIPTOR' : _CONNECTIONID, - '__module__' : 'service_pb2' - # @@protoc_insertion_point(class_scope:service.ConnectionId) - }) -_sym_db.RegisterMessage(ConnectionId) - _SERVICESERVICE = _descriptor.ServiceDescriptor( @@ -546,69 +37,39 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=990, - serialized_end=1355, + serialized_start=42, + serialized_end=227, methods=[ - _descriptor.MethodDescriptor( - name='GetServiceList', - full_name='service.ServiceService.GetServiceList', - index=0, - containing_service=None, - input_type=context__pb2._EMPTY, - output_type=_SERVICELIST, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), _descriptor.MethodDescriptor( name='CreateService', full_name='service.ServiceService.CreateService', - index=1, + index=0, containing_service=None, - input_type=_SERVICE, - output_type=_SERVICEID, + input_type=context__pb2._SERVICE, + output_type=context__pb2._SERVICEID, serialized_options=None, create_key=_descriptor._internal_create_key, ), _descriptor.MethodDescriptor( name='UpdateService', full_name='service.ServiceService.UpdateService', - index=2, + index=1, containing_service=None, - input_type=_SERVICE, - output_type=_SERVICEID, + input_type=context__pb2._SERVICE, + output_type=context__pb2._SERVICEID, serialized_options=None, create_key=_descriptor._internal_create_key, ), _descriptor.MethodDescriptor( name='DeleteService', full_name='service.ServiceService.DeleteService', - index=3, + index=2, containing_service=None, - input_type=_SERVICEID, + input_type=context__pb2._SERVICEID, output_type=context__pb2._EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, ), - _descriptor.MethodDescriptor( - name='GetServiceById', - full_name='service.ServiceService.GetServiceById', - index=4, - containing_service=None, - input_type=_SERVICEID, - output_type=_SERVICE, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), - _descriptor.MethodDescriptor( - name='GetConnectionList', - full_name='service.ServiceService.GetConnectionList', - index=5, - containing_service=None, - input_type=context__pb2._EMPTY, - output_type=_CONNECTIONLIST, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), ]) _sym_db.RegisterServiceDescriptor(_SERVICESERVICE) diff --git a/src/slice/proto/slice_pb2.py b/src/slice/proto/slice_pb2.py index 06160c426..91dbaaae3 100644 --- a/src/slice/proto/slice_pb2.py +++ b/src/slice/proto/slice_pb2.py @@ -13,7 +13,6 @@ _sym_db = _symbol_database.Default() from . import context_pb2 as context__pb2 -from . import service_pb2 as service__pb2 DESCRIPTOR = _descriptor.FileDescriptor( @@ -22,9 +21,9 @@ DESCRIPTOR = _descriptor.FileDescriptor( 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' + serialized_pb=b'\n\x0bslice.proto\x12\x05slice\x1a\rcontext.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.context.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,]) + dependencies=[context__pb2.DESCRIPTOR,]) _SLICESTATUSENUM = _descriptor.EnumDescriptor( name='SliceStatusEnum', @@ -56,8 +55,8 @@ _SLICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=524, - serialized_end=588, + serialized_start=509, + serialized_end=573, ) _sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) @@ -96,8 +95,8 @@ _SLICEENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=52, - serialized_end=103, + serialized_start=37, + serialized_end=88, ) @@ -163,8 +162,8 @@ _TRANSPORTSLICE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=106, - serialized_end=350, + serialized_start=91, + serialized_end=335, ) @@ -202,8 +201,8 @@ _SLICEID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=352, - serialized_end=433, + serialized_start=337, + serialized_end=418, ) @@ -241,15 +240,15 @@ _SLICESTATUS = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=435, - serialized_end=522, + serialized_start=420, + serialized_end=507, ) _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['services'].message_type = context__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 @@ -300,8 +299,8 @@ _SLICESERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=591, - serialized_end=727, + serialized_start=576, + serialized_end=712, methods=[ _descriptor.MethodDescriptor( name='CreateUpdateSlice', diff --git a/src/slice/requirements.in b/src/slice/requirements.in index 25abdad1b..58c398e7d 100644 --- a/src/slice/requirements.in +++ b/src/slice/requirements.in @@ -1,6 +1,5 @@ -grpcio-health-checking -grpcio -prometheus-client -pytest -pytest-benchmark -redis +grpcio==1.43.0 +grpcio-health-checking==1.43.0 +prometheus-client==0.13.0 +pytest==6.2.5 +pytest-benchmark==3.4.1 diff --git a/src/slice/service/SliceService.py b/src/slice/service/SliceService.py index 2335cdd31..cddc4efe9 100644 --- a/src/slice/service/SliceService.py +++ b/src/slice/service/SliceService.py @@ -1,5 +1,18 @@ -import grpc -import logging +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 grpc, logging from concurrent import futures from grpc_health.v1.health import HealthServicer, OVERALL_HEALTH from grpc_health.v1.health_pb2 import HealthCheckResponse @@ -12,9 +25,10 @@ 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 + 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 @@ -26,14 +40,16 @@ class SliceService: 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.endpoint = '{:s}:{:s}'.format(str(self.address), str(self.port)) + LOGGER.info('Starting Service (tentative endpoint: {:s}, max_workers: {:s})...'.format( + str(self.endpoint), str(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) + self.slice_servicer = SliceServiceServicerImpl( + #self.database + ) add_SliceServiceServicer_to_server(self.slice_servicer, self.server) self.health_servicer = HealthServicer( @@ -41,15 +57,15 @@ class SliceService: 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.endpoint = '{:s}:{:s}'.format(str(self.address), str(port)) + LOGGER.info('Listening on {:s}...'.format(str(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)) + LOGGER.debug('Stopping service (grace period {:s} seconds)...'.format(str(self.grace_period))) self.health_servicer.enter_graceful_shutdown() self.server.stop(self.grace_period) LOGGER.debug('Service stopped') diff --git a/src/slice/service/SliceServiceServicerImpl.py b/src/slice/service/SliceServiceServicerImpl.py index fbbf607df..702e0ce38 100644 --- a/src/slice/service/SliceServiceServicerImpl.py +++ b/src/slice/service/SliceServiceServicerImpl.py @@ -1,95 +1,38 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 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 common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method 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') +SERVICE_NAME = 'Slice' +METHOD_NAMES = ['CreateUpdateSlice', 'DeleteSlice'] +METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) class SliceServiceServicerImpl(SliceServiceServicer): - def __init__(self, database : Database): - LOGGER.info('Creating Servicer...') - self.database = database - LOGGER.info('Servicer Created') - - @CREATEUPDATESLICE_HISTOGRAM_DURATION.time() - def CreateUpdateSlice(self, request : TransportSlice, grpc_context : grpc.ServicerContext) -> SliceStatus: - CREATEUPDATESLICE_COUNTER_STARTED.inc() - try: - LOGGER.info('CreateUpdateSlice request: {}'.format(str(request))) - LOGGER.warning('NOT IMPLEMENTED') - - ## ----- 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() #SliceStatus(**db_device.dump_id()) - LOGGER.info('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.info('DeleteSlice request: {}'.format(str(request))) - LOGGER.warning('NOT IMPLEMENTED') - - ## ----- Validate request data and pre-conditions ----------------------------------------------------------- - #device_id = check_slice_id_request('DeleteSlice', request, self.database, LOGGER) + def __init__(self): + LOGGER.debug('Creating Servicer...') + LOGGER.debug('Servicer Created') - ## ----- 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() + @safe_and_metered_rpc_method(METRICS, LOGGER) + def CreateUpdateSlice(self, request : TransportSlice, context : grpc.ServicerContext) -> SliceStatus: + return SliceStatus() - # ----- Compose reply -------------------------------------------------------------------------------------- - reply = Empty() - LOGGER.info('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)) + @safe_and_metered_rpc_method(METRICS, LOGGER) + def DeleteSlice(self, request : TransportSlice, context : grpc.ServicerContext) -> Empty: + return Empty() diff --git a/src/slice/service/__main__.py b/src/slice/service/__main__.py index f3979d188..54bd059d2 100644 --- a/src/slice/service/__main__.py +++ b/src/slice/service/__main__.py @@ -1,51 +1,89 @@ -import logging, os, signal, sys, threading +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, signal, sys, threading from prometheus_client import start_http_server -from common.database.Factory import get_database -from slice.service.SliceService import SliceService +from common.Settings import get_setting, wait_for_environment_variables +#from common.database.Factory import get_database from slice.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, METRICS_PORT +from .SliceService import SliceService terminate = threading.Event() -logger = None +LOGGER : logging.Logger = None -def signal_handler(signal, frame): - global terminate, logger - logger.warning('Terminate signal received') +def signal_handler(signal, frame): # pylint: disable=redefined-outer-name + LOGGER.warning('Terminate signal received') terminate.set() def main(): - global terminate, logger + global LOGGER # pylint: disable=global-statement - 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 ) + grpc_service_port = get_setting('SLICESERVICE_SERVICE_PORT_GRPC', default=GRPC_SERVICE_PORT ) + max_workers = get_setting('MAX_WORKERS', default=GRPC_MAX_WORKERS ) + grace_period = get_setting('GRACE_PERIOD', default=GRPC_GRACE_PERIOD ) + log_level = get_setting('LOG_LEVEL', default=LOG_LEVEL ) + metrics_port = get_setting('METRICS_PORT', default=METRICS_PORT ) logging.basicConfig(level=log_level) - logger = logging.getLogger(__name__) + LOGGER = logging.getLogger(__name__) + + wait_for_environment_variables([ + #'CONTEXTSERVICE_SERVICE_HOST', 'CONTEXTSERVICE_SERVICE_PORT_GRPC', + #'MONITORINGSERVICE_SERVICE_HOST', 'MONITORINGSERVICE_SERVICE_PORT_GRPC' + ]) + + #context_service_host = get_setting('CONTEXTSERVICE_SERVICE_HOST', default=CONTEXT_SERVICE_HOST ) + #context_service_port = get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC', default=CONTEXT_SERVICE_PORT ) + #monitoring_service_host = get_setting('MONITORINGSERVICE_SERVICE_HOST', default=MONITORING_SERVICE_HOST) + #monitoring_service_port = get_setting('MONITORINGSERVICE_SERVICE_PORT_GRPC', default=MONITORING_SERVICE_PORT) signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) - logger.info('Starting...') + LOGGER.info('Starting...') # Start metrics server start_http_server(metrics_port) - # Get database instance - database = get_database() + ## Initialize Context Client + #if context_service_host is None or context_service_port is None: + # raise Exception('Wrong address({:s}):port({:s}) of Context component'.format( + # str(context_service_host), str(context_service_port))) + #context_client = ContextClient(context_service_host, context_service_port) + + ## Initialize Monitoring Client + #if monitoring_service_host is None or monitoring_service_port is None: + # raise Exception('Wrong address({:s}):port({:s}) of Monitoring component'.format( + # str(monitoring_service_host), str(monitoring_service_port))) + #monitoring_client = MonitoringClient(monitoring_service_host, monitoring_service_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) + # Starting slice service + grpc_service = SliceService( + #database, + port=grpc_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...') + LOGGER.info('Terminating...') grpc_service.stop() - logger.info('Bye') + LOGGER.info('Bye') return 0 if __name__ == '__main__': -- GitLab From f7d6a431ce701e65211f08ff58536aced4077482 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Thu, 10 Mar 2022 14:35:33 +0100 Subject: [PATCH 011/353] Updated proto files: - formatted and corrected interdomain and slice proto files - regenerated associated Python code files per component --- proto/context.proto | 39 ++ proto/interdomain.proto | 7 +- proto/slice.proto | 34 +- src/compute/proto/__init__.py | 14 - src/compute/proto/context_pb2.py | 423 ++++++++++++++++-- src/compute/proto/service_pb2.py | 14 +- src/context/proto/__init__.py | 14 - src/context/proto/context_pb2.py | 423 ++++++++++++++++-- src/dbscanserving/proto/__init__.py | 14 - src/dbscanserving/proto/dbscanserving_pb2.py | 24 +- src/device/proto/__init__.py | 14 - src/device/proto/context_pb2.py | 423 ++++++++++++++++-- src/interdomain/proto/context_pb2.py | 423 ++++++++++++++++-- src/interdomain/proto/interdomain_pb2.py | 21 +- src/interdomain/proto/interdomain_pb2_grpc.py | 37 +- src/interdomain/proto/slice_pb2.py | 274 +----------- src/l3_attackmitigator/genproto.sh | 0 src/l3_attackmitigator/proto/__init__.py | 14 - .../proto/l3_attackmitigator_pb2.py | 132 ++---- .../proto/l3_attackmitigator_pb2_grpc.py | 26 +- src/l3_centralizedattackdetector/genproto.sh | 0 .../proto/__init__.py | 14 - .../proto/l3_attackmitigator_pb2.py | 132 ++---- .../proto/l3_attackmitigator_pb2_grpc.py | 26 +- .../proto/l3_centralizedattackdetector_pb2.py | 154 +++---- .../l3_centralizedattackdetector_pb2_grpc.py | 12 +- src/l3_distributedattackdetector/genproto.sh | 0 .../proto/__init__.py | 14 - .../proto/l3_centralizedattackdetector_pb2.py | 154 +++---- .../l3_centralizedattackdetector_pb2_grpc.py | 12 +- src/monitoring/proto/__init__.py | 14 - src/monitoring/proto/context_pb2.py | 423 ++++++++++++++++-- src/opticalattackmitigator/proto/__init__.py | 14 - .../proto/context_pb2.py | 423 ++++++++++++++++-- .../proto/__init__.py | 14 - .../proto/context_pb2.py | 423 ++++++++++++++++-- .../proto/service_pb2.py | 14 +- src/service/proto/__init__.py | 14 - src/service/proto/context_pb2.py | 423 ++++++++++++++++-- src/service/proto/service_pb2.py | 14 +- src/service/proto/service_pb2_grpc.py | 33 -- src/slice/proto/context_pb2.py | 423 ++++++++++++++++-- src/slice/proto/slice_pb2.py | 274 +----------- src/slice/proto/slice_pb2_grpc.py | 19 +- src/webui/proto/__init__.py | 14 - src/webui/proto/context_pb2.py | 423 ++++++++++++++++-- src/webui/proto/service_pb2.py | 14 +- 47 files changed, 4175 insertions(+), 1689 deletions(-) mode change 100644 => 100755 src/l3_attackmitigator/genproto.sh mode change 100644 => 100755 src/l3_centralizedattackdetector/genproto.sh mode change 100644 => 100755 src/l3_distributedattackdetector/genproto.sh diff --git a/proto/context.proto b/proto/context.proto index 057f44c9b..346708c43 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -253,6 +253,45 @@ message ServiceEvent { ServiceId service_id = 2; } +// ----- Slice --------------------------------------------------------------------------------------------------------- +message SliceId { + ContextId context_id = 1; + Uuid slice_uuid = 2; +} + +message Slice { + SliceId slice_id = 1; + repeated EndPointId slice_endpoint_ids = 2; + repeated Constraint slice_constraints = 3; + repeated ServiceId slice_services = 4; + repeated SliceId slice_subslice_ids = 5; + SliceStatus slice_status = 6; +} + +enum SliceStatusEnum { + SLICESTATUS_UNDEFINED = 0; + SLICESTATUS_PLANNED = 1; + SLICESTATUS_INIT = 2; + SLICESTATUS_ACTIVE = 3; + SLICESTATUS_DEINIT = 4; +} + +message SliceStatus { + SliceStatusEnum slice_status = 1; +} + +message SliceIdList { + repeated SliceId slice_ids = 1; +} + +message SliceList { + repeated Slice slices = 1; +} + +message SliceEvent { + Event event = 1; + SliceId slice_id = 2; +} // ----- Connection ---------------------------------------------------------------------------------------------------- message ConnectionId { diff --git a/proto/interdomain.proto b/proto/interdomain.proto index 80fe07469..9b02c2918 100644 --- a/proto/interdomain.proto +++ b/proto/interdomain.proto @@ -16,11 +16,10 @@ syntax = "proto3"; package interdomain; import "context.proto"; -import "slice.proto"; service InterdomainService { rpc Authenticate (context.TeraFlowController) returns (context.AuthenticationResult) {} - rpc LookUpSlice (slice.TransportSlice ) returns (slice.SliceId ) {} - rpc OrderSliceFromCatalog (slice.TransportSlice ) returns (slice.SliceStatus ) {} - rpc CreateSliceAndAddToCatalog(slice.TransportSlice ) returns (slice.SliceStatus ) {} + rpc LookUpSlice (context.Slice ) returns (context.SliceId ) {} + rpc OrderSliceFromCatalog (context.Slice ) returns (context.Slice ) {} + rpc CreateSliceAndAddToCatalog(context.Slice ) returns (context.Slice ) {} } diff --git a/proto/slice.proto b/proto/slice.proto index 43f3329d4..ef6567d78 100644 --- a/proto/slice.proto +++ b/proto/slice.proto @@ -18,36 +18,6 @@ package slice; import "context.proto"; service SliceService { - rpc CreateUpdateSlice (TransportSlice) returns (SliceStatus ) {} - rpc DeleteSlice (TransportSlice) returns (context.Empty) {} -} - -message SliceEndpoint { - context.EndPoint port_id = 1; -} - -message TransportSlice { - SliceId slice_id = 1; - repeated slice.SliceEndpoint endpoints = 2; - repeated context.Constraint constraints = 3; - repeated context.ServiceId services = 4; - repeated SliceId subSlicesId = 5; - SliceStatus status = 6; -} - -message SliceId { - context.ContextId contextId = 1; - context.Uuid slice_id = 2; -} - -message SliceStatus { - slice.SliceId slice_id = 1; - SliceStatusEnum status = 2; -} - -enum SliceStatusEnum { - PLANNED = 0; - INIT = 1; - ACTIVE = 2; - DEINIT = 3; + rpc CreateUpdateSlice (context.Slice) returns (context.SliceId) {} + rpc DeleteSlice (context.Slice) returns (context.Empty ) {} } diff --git a/src/compute/proto/__init__.py b/src/compute/proto/__init__.py index 70a332512..e69de29bb 100644 --- a/src/compute/proto/__init__.py +++ b/src/compute/proto/__init__.py @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/compute/proto/context_pb2.py b/src/compute/proto/context_pb2.py index 68602b16f..3e1b4c54d 100644 --- a/src/compute/proto/context_pb2.py +++ b/src/compute/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3703, - serialized_end=3809, + serialized_start=4307, + serialized_end=4413, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3812, - serialized_end=4009, + serialized_start=4416, + serialized_end=4613, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4012, - serialized_end=4155, + serialized_start=4616, + serialized_end=4759, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4158, - serialized_end=4287, + serialized_start=4762, + serialized_end=4891, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,12 +204,53 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4290, - serialized_end=4426, + serialized_start=4894, + serialized_end=5030, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_SLICESTATUSENUM = _descriptor.EnumDescriptor( + name='SliceStatusEnum', + full_name='context.SliceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_INIT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_ACTIVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_DEINIT', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5033, + serialized_end=5172, +) +_sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) + +SliceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SLICESTATUSENUM) _CONFIGACTIONENUM = _descriptor.EnumDescriptor( name='ConfigActionEnum', full_name='context.ConfigActionEnum', @@ -235,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4428, - serialized_end=4521, + serialized_start=5174, + serialized_end=5267, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -262,6 +303,11 @@ SERVICESTATUS_UNDEFINED = 0 SERVICESTATUS_PLANNED = 1 SERVICESTATUS_ACTIVE = 2 SERVICESTATUS_PENDING_REMOVAL = 3 +SLICESTATUS_UNDEFINED = 0 +SLICESTATUS_PLANNED = 1 +SLICESTATUS_INIT = 2 +SLICESTATUS_ACTIVE = 3 +SLICESTATUS_DEINIT = 4 CONFIGACTION_UNDEFINED = 0 CONFIGACTION_SET = 1 CONFIGACTION_DELETE = 2 @@ -1421,6 +1467,247 @@ _SERVICEEVENT = _descriptor.Descriptor( ) +_SLICEID = _descriptor.Descriptor( + name='SliceId', + full_name='context.SliceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.SliceId.context_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='slice_uuid', full_name='context.SliceId.slice_uuid', 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=2604, + serialized_end=2688, +) + + +_SLICE = _descriptor.Descriptor( + name='Slice', + full_name='context.Slice', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_id', full_name='context.Slice.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='slice_endpoint_ids', full_name='context.Slice.slice_endpoint_ids', 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='slice_constraints', full_name='context.Slice.slice_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='slice_services', full_name='context.Slice.slice_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='slice_subslice_ids', full_name='context.Slice.slice_subslice_ids', 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='slice_status', full_name='context.Slice.slice_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=2691, + serialized_end=2965, +) + + +_SLICESTATUS = _descriptor.Descriptor( + name='SliceStatus', + full_name='context.SliceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_status', full_name='context.SliceStatus.slice_status', index=0, + number=1, 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=2967, + serialized_end=3028, +) + + +_SLICEIDLIST = _descriptor.Descriptor( + name='SliceIdList', + full_name='context.SliceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_ids', full_name='context.SliceIdList.slice_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3030, + serialized_end=3080, +) + + +_SLICELIST = _descriptor.Descriptor( + name='SliceList', + full_name='context.SliceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slices', full_name='context.SliceList.slices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3082, + serialized_end=3125, +) + + +_SLICEEVENT = _descriptor.Descriptor( + name='SliceEvent', + full_name='context.SliceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.SliceEvent.event', 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='context.SliceEvent.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=3127, + serialized_end=3206, +) + + _CONNECTIONID = _descriptor.Descriptor( name='ConnectionId', full_name='context.ConnectionId', @@ -1448,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2604, - serialized_end=2658, + serialized_start=3208, + serialized_end=3262, ) @@ -1501,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2661, - serialized_end=2857, + serialized_start=3265, + serialized_end=3461, ) @@ -1533,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2859, - serialized_end=2924, + serialized_start=3463, + serialized_end=3528, ) @@ -1565,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2926, - serialized_end=2984, + serialized_start=3530, + serialized_end=3588, ) @@ -1604,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2986, - serialized_end=3080, + serialized_start=3590, + serialized_end=3684, ) @@ -1650,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3083, - serialized_end=3213, + serialized_start=3687, + serialized_end=3817, ) @@ -1696,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3216, - serialized_end=3350, + serialized_start=3820, + serialized_end=3954, ) @@ -1742,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3352, - serialized_end=3453, + serialized_start=3956, + serialized_end=4057, ) @@ -1781,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3455, - serialized_end=3518, + serialized_start=4059, + serialized_end=4122, ) @@ -1827,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3520, - serialized_end=3614, + serialized_start=4124, + serialized_end=4218, ) @@ -1866,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3616, - serialized_end=3701, + serialized_start=4220, + serialized_end=4305, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -1921,6 +2208,19 @@ _SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID _SERVICELIST.fields_by_name['services'].message_type = _SERVICE _SERVICEEVENT.fields_by_name['event'].message_type = _EVENT _SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_SLICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SLICEID.fields_by_name['slice_uuid'].message_type = _UUID +_SLICE.fields_by_name['slice_id'].message_type = _SLICEID +_SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID +_SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT +_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID +_SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS +_SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM +_SLICEIDLIST.fields_by_name['slice_ids'].message_type = _SLICEID +_SLICELIST.fields_by_name['slices'].message_type = _SLICE +_SLICEEVENT.fields_by_name['event'].message_type = _EVENT +_SLICEEVENT.fields_by_name['slice_id'].message_type = _SLICEID _CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID _CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID _CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID @@ -1969,6 +2269,12 @@ DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['SliceId'] = _SLICEID +DESCRIPTOR.message_types_by_name['Slice'] = _SLICE +DESCRIPTOR.message_types_by_name['SliceStatus'] = _SLICESTATUS +DESCRIPTOR.message_types_by_name['SliceIdList'] = _SLICEIDLIST +DESCRIPTOR.message_types_by_name['SliceList'] = _SLICELIST +DESCRIPTOR.message_types_by_name['SliceEvent'] = _SLICEEVENT DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST @@ -1985,6 +2291,7 @@ DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['SliceStatusEnum'] = _SLICESTATUSENUM DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -2205,6 +2512,48 @@ ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_messag }) _sym_db.RegisterMessage(ServiceEvent) +SliceId = _reflection.GeneratedProtocolMessageType('SliceId', (_message.Message,), { + 'DESCRIPTOR' : _SLICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceId) + }) +_sym_db.RegisterMessage(SliceId) + +Slice = _reflection.GeneratedProtocolMessageType('Slice', (_message.Message,), { + 'DESCRIPTOR' : _SLICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Slice) + }) +_sym_db.RegisterMessage(Slice) + +SliceStatus = _reflection.GeneratedProtocolMessageType('SliceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SLICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceStatus) + }) +_sym_db.RegisterMessage(SliceStatus) + +SliceIdList = _reflection.GeneratedProtocolMessageType('SliceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SLICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceIdList) + }) +_sym_db.RegisterMessage(SliceIdList) + +SliceList = _reflection.GeneratedProtocolMessageType('SliceList', (_message.Message,), { + 'DESCRIPTOR' : _SLICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceList) + }) +_sym_db.RegisterMessage(SliceList) + +SliceEvent = _reflection.GeneratedProtocolMessageType('SliceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SLICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceEvent) + }) +_sym_db.RegisterMessage(SliceEvent) + ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { 'DESCRIPTOR' : _CONNECTIONID, '__module__' : 'context_pb2' @@ -2291,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=4524, - serialized_end=6617, + serialized_start=5270, + serialized_end=7363, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/compute/proto/service_pb2.py b/src/compute/proto/service_pb2.py index 7a006915b..8e2806c76 100644 --- a/src/compute/proto/service_pb2.py +++ b/src/compute/proto/service_pb2.py @@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xfd\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12\x42\n\x11GetConnectionList\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x62\x06proto3' + serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xb9\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x62\x06proto3' , dependencies=[context__pb2.DESCRIPTOR,]) @@ -38,7 +38,7 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=42, - serialized_end=295, + serialized_end=227, methods=[ _descriptor.MethodDescriptor( name='CreateService', @@ -70,16 +70,6 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), - _descriptor.MethodDescriptor( - name='GetConnectionList', - full_name='service.ServiceService.GetConnectionList', - index=3, - containing_service=None, - input_type=context__pb2._SERVICEID, - output_type=context__pb2._CONNECTIONLIST, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), ]) _sym_db.RegisterServiceDescriptor(_SERVICESERVICE) diff --git a/src/context/proto/__init__.py b/src/context/proto/__init__.py index 70a332512..e69de29bb 100644 --- a/src/context/proto/__init__.py +++ b/src/context/proto/__init__.py @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/context/proto/context_pb2.py b/src/context/proto/context_pb2.py index 68602b16f..3e1b4c54d 100644 --- a/src/context/proto/context_pb2.py +++ b/src/context/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3703, - serialized_end=3809, + serialized_start=4307, + serialized_end=4413, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3812, - serialized_end=4009, + serialized_start=4416, + serialized_end=4613, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4012, - serialized_end=4155, + serialized_start=4616, + serialized_end=4759, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4158, - serialized_end=4287, + serialized_start=4762, + serialized_end=4891, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,12 +204,53 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4290, - serialized_end=4426, + serialized_start=4894, + serialized_end=5030, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_SLICESTATUSENUM = _descriptor.EnumDescriptor( + name='SliceStatusEnum', + full_name='context.SliceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_INIT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_ACTIVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_DEINIT', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5033, + serialized_end=5172, +) +_sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) + +SliceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SLICESTATUSENUM) _CONFIGACTIONENUM = _descriptor.EnumDescriptor( name='ConfigActionEnum', full_name='context.ConfigActionEnum', @@ -235,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4428, - serialized_end=4521, + serialized_start=5174, + serialized_end=5267, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -262,6 +303,11 @@ SERVICESTATUS_UNDEFINED = 0 SERVICESTATUS_PLANNED = 1 SERVICESTATUS_ACTIVE = 2 SERVICESTATUS_PENDING_REMOVAL = 3 +SLICESTATUS_UNDEFINED = 0 +SLICESTATUS_PLANNED = 1 +SLICESTATUS_INIT = 2 +SLICESTATUS_ACTIVE = 3 +SLICESTATUS_DEINIT = 4 CONFIGACTION_UNDEFINED = 0 CONFIGACTION_SET = 1 CONFIGACTION_DELETE = 2 @@ -1421,6 +1467,247 @@ _SERVICEEVENT = _descriptor.Descriptor( ) +_SLICEID = _descriptor.Descriptor( + name='SliceId', + full_name='context.SliceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.SliceId.context_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='slice_uuid', full_name='context.SliceId.slice_uuid', 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=2604, + serialized_end=2688, +) + + +_SLICE = _descriptor.Descriptor( + name='Slice', + full_name='context.Slice', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_id', full_name='context.Slice.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='slice_endpoint_ids', full_name='context.Slice.slice_endpoint_ids', 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='slice_constraints', full_name='context.Slice.slice_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='slice_services', full_name='context.Slice.slice_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='slice_subslice_ids', full_name='context.Slice.slice_subslice_ids', 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='slice_status', full_name='context.Slice.slice_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=2691, + serialized_end=2965, +) + + +_SLICESTATUS = _descriptor.Descriptor( + name='SliceStatus', + full_name='context.SliceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_status', full_name='context.SliceStatus.slice_status', index=0, + number=1, 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=2967, + serialized_end=3028, +) + + +_SLICEIDLIST = _descriptor.Descriptor( + name='SliceIdList', + full_name='context.SliceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_ids', full_name='context.SliceIdList.slice_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3030, + serialized_end=3080, +) + + +_SLICELIST = _descriptor.Descriptor( + name='SliceList', + full_name='context.SliceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slices', full_name='context.SliceList.slices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3082, + serialized_end=3125, +) + + +_SLICEEVENT = _descriptor.Descriptor( + name='SliceEvent', + full_name='context.SliceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.SliceEvent.event', 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='context.SliceEvent.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=3127, + serialized_end=3206, +) + + _CONNECTIONID = _descriptor.Descriptor( name='ConnectionId', full_name='context.ConnectionId', @@ -1448,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2604, - serialized_end=2658, + serialized_start=3208, + serialized_end=3262, ) @@ -1501,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2661, - serialized_end=2857, + serialized_start=3265, + serialized_end=3461, ) @@ -1533,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2859, - serialized_end=2924, + serialized_start=3463, + serialized_end=3528, ) @@ -1565,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2926, - serialized_end=2984, + serialized_start=3530, + serialized_end=3588, ) @@ -1604,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2986, - serialized_end=3080, + serialized_start=3590, + serialized_end=3684, ) @@ -1650,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3083, - serialized_end=3213, + serialized_start=3687, + serialized_end=3817, ) @@ -1696,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3216, - serialized_end=3350, + serialized_start=3820, + serialized_end=3954, ) @@ -1742,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3352, - serialized_end=3453, + serialized_start=3956, + serialized_end=4057, ) @@ -1781,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3455, - serialized_end=3518, + serialized_start=4059, + serialized_end=4122, ) @@ -1827,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3520, - serialized_end=3614, + serialized_start=4124, + serialized_end=4218, ) @@ -1866,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3616, - serialized_end=3701, + serialized_start=4220, + serialized_end=4305, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -1921,6 +2208,19 @@ _SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID _SERVICELIST.fields_by_name['services'].message_type = _SERVICE _SERVICEEVENT.fields_by_name['event'].message_type = _EVENT _SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_SLICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SLICEID.fields_by_name['slice_uuid'].message_type = _UUID +_SLICE.fields_by_name['slice_id'].message_type = _SLICEID +_SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID +_SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT +_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID +_SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS +_SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM +_SLICEIDLIST.fields_by_name['slice_ids'].message_type = _SLICEID +_SLICELIST.fields_by_name['slices'].message_type = _SLICE +_SLICEEVENT.fields_by_name['event'].message_type = _EVENT +_SLICEEVENT.fields_by_name['slice_id'].message_type = _SLICEID _CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID _CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID _CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID @@ -1969,6 +2269,12 @@ DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['SliceId'] = _SLICEID +DESCRIPTOR.message_types_by_name['Slice'] = _SLICE +DESCRIPTOR.message_types_by_name['SliceStatus'] = _SLICESTATUS +DESCRIPTOR.message_types_by_name['SliceIdList'] = _SLICEIDLIST +DESCRIPTOR.message_types_by_name['SliceList'] = _SLICELIST +DESCRIPTOR.message_types_by_name['SliceEvent'] = _SLICEEVENT DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST @@ -1985,6 +2291,7 @@ DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['SliceStatusEnum'] = _SLICESTATUSENUM DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -2205,6 +2512,48 @@ ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_messag }) _sym_db.RegisterMessage(ServiceEvent) +SliceId = _reflection.GeneratedProtocolMessageType('SliceId', (_message.Message,), { + 'DESCRIPTOR' : _SLICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceId) + }) +_sym_db.RegisterMessage(SliceId) + +Slice = _reflection.GeneratedProtocolMessageType('Slice', (_message.Message,), { + 'DESCRIPTOR' : _SLICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Slice) + }) +_sym_db.RegisterMessage(Slice) + +SliceStatus = _reflection.GeneratedProtocolMessageType('SliceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SLICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceStatus) + }) +_sym_db.RegisterMessage(SliceStatus) + +SliceIdList = _reflection.GeneratedProtocolMessageType('SliceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SLICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceIdList) + }) +_sym_db.RegisterMessage(SliceIdList) + +SliceList = _reflection.GeneratedProtocolMessageType('SliceList', (_message.Message,), { + 'DESCRIPTOR' : _SLICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceList) + }) +_sym_db.RegisterMessage(SliceList) + +SliceEvent = _reflection.GeneratedProtocolMessageType('SliceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SLICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceEvent) + }) +_sym_db.RegisterMessage(SliceEvent) + ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { 'DESCRIPTOR' : _CONNECTIONID, '__module__' : 'context_pb2' @@ -2291,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=4524, - serialized_end=6617, + serialized_start=5270, + serialized_end=7363, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/dbscanserving/proto/__init__.py b/src/dbscanserving/proto/__init__.py index 70a332512..e69de29bb 100644 --- a/src/dbscanserving/proto/__init__.py +++ b/src/dbscanserving/proto/__init__.py @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/dbscanserving/proto/dbscanserving_pb2.py b/src/dbscanserving/proto/dbscanserving_pb2.py index b5e464db4..f2d6c37c7 100644 --- a/src/dbscanserving/proto/dbscanserving_pb2.py +++ b/src/dbscanserving/proto/dbscanserving_pb2.py @@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x13\x64\x62scanserving.proto\x12\rdbscanserving\"\x1a\n\x06Sample\x12\x10\n\x08\x66\x65\x61tures\x18\x01 \x03(\x02\"\xd6\x01\n\x10\x44\x65tectionRequest\x12\x0b\n\x03\x65ps\x18\x01 \x01(\x02\x12\x13\n\x0bmin_samples\x18\x02 \x01(\x05\x12%\n\x06metric\x18\x03 \x01(\x0e\x32\x15.dbscanserving.Metric\x12\x13\n\x0bnum_samples\x18\x04 \x01(\x05\x12\x14\n\x0cnum_features\x18\x05 \x01(\x05\x12&\n\x07samples\x18\x06 \x03(\x0b\x32\x15.dbscanserving.Sample\x12\x17\n\nidentifier\x18\x07 \x01(\x05H\x00\x88\x01\x01\x42\r\n\x0b_identifier\",\n\x11\x44\x65tectionResponse\x12\x17\n\x0f\x63luster_indices\x18\x01 \x03(\x05*\x17\n\x06Metric\x12\r\n\tEUCLIDEAN\x10\x00\x32W\n\x08\x44\x65tector\x12K\n\x06\x44\x65tect\x12\x1f.dbscanserving.DetectionRequest\x1a .dbscanserving.DetectionResponseb\x06proto3' + serialized_pb=b'\n\x13\x64\x62scanserving.proto\x12\rdbscanserving\"\x1a\n\x06Sample\x12\x10\n\x08\x66\x65\x61tures\x18\x01 \x03(\x02\"\xc2\x01\n\x10\x44\x65tectionRequest\x12\x0b\n\x03\x65ps\x18\x01 \x01(\x02\x12\x13\n\x0bmin_samples\x18\x02 \x01(\x05\x12%\n\x06metric\x18\x03 \x01(\x0e\x32\x15.dbscanserving.Metric\x12\x13\n\x0bnum_samples\x18\x04 \x01(\x05\x12\x14\n\x0cnum_features\x18\x05 \x01(\x05\x12&\n\x07samples\x18\x06 \x03(\x0b\x32\x15.dbscanserving.Sample\x12\x12\n\nidentifier\x18\x07 \x01(\x05\",\n\x11\x44\x65tectionResponse\x12\x17\n\x0f\x63luster_indices\x18\x01 \x03(\x05*\x17\n\x06Metric\x12\r\n\tEUCLIDEAN\x10\x00\x32W\n\x08\x44\x65tector\x12K\n\x06\x44\x65tect\x12\x1f.dbscanserving.DetectionRequest\x1a .dbscanserving.DetectionResponseb\x06proto3' ) _METRIC = _descriptor.EnumDescriptor( @@ -38,8 +38,8 @@ _METRIC = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=329, - serialized_end=352, + serialized_start=309, + serialized_end=332, ) _sym_db.RegisterEnumDescriptor(_METRIC) @@ -148,14 +148,9 @@ _DETECTIONREQUEST = _descriptor.Descriptor( syntax='proto3', extension_ranges=[], oneofs=[ - _descriptor.OneofDescriptor( - name='_identifier', full_name='dbscanserving.DetectionRequest._identifier', - index=0, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), ], serialized_start=67, - serialized_end=281, + serialized_end=261, ) @@ -186,15 +181,12 @@ _DETECTIONRESPONSE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=283, - serialized_end=327, + serialized_start=263, + serialized_end=307, ) _DETECTIONREQUEST.fields_by_name['metric'].enum_type = _METRIC _DETECTIONREQUEST.fields_by_name['samples'].message_type = _SAMPLE -_DETECTIONREQUEST.oneofs_by_name['_identifier'].fields.append( - _DETECTIONREQUEST.fields_by_name['identifier']) -_DETECTIONREQUEST.fields_by_name['identifier'].containing_oneof = _DETECTIONREQUEST.oneofs_by_name['_identifier'] DESCRIPTOR.message_types_by_name['Sample'] = _SAMPLE DESCRIPTOR.message_types_by_name['DetectionRequest'] = _DETECTIONREQUEST DESCRIPTOR.message_types_by_name['DetectionResponse'] = _DETECTIONRESPONSE @@ -231,8 +223,8 @@ _DETECTOR = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=354, - serialized_end=441, + serialized_start=334, + serialized_end=421, methods=[ _descriptor.MethodDescriptor( name='Detect', diff --git a/src/device/proto/__init__.py b/src/device/proto/__init__.py index 70a332512..e69de29bb 100644 --- a/src/device/proto/__init__.py +++ b/src/device/proto/__init__.py @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/device/proto/context_pb2.py b/src/device/proto/context_pb2.py index 68602b16f..3e1b4c54d 100644 --- a/src/device/proto/context_pb2.py +++ b/src/device/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3703, - serialized_end=3809, + serialized_start=4307, + serialized_end=4413, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3812, - serialized_end=4009, + serialized_start=4416, + serialized_end=4613, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4012, - serialized_end=4155, + serialized_start=4616, + serialized_end=4759, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4158, - serialized_end=4287, + serialized_start=4762, + serialized_end=4891, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,12 +204,53 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4290, - serialized_end=4426, + serialized_start=4894, + serialized_end=5030, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_SLICESTATUSENUM = _descriptor.EnumDescriptor( + name='SliceStatusEnum', + full_name='context.SliceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_INIT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_ACTIVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_DEINIT', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5033, + serialized_end=5172, +) +_sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) + +SliceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SLICESTATUSENUM) _CONFIGACTIONENUM = _descriptor.EnumDescriptor( name='ConfigActionEnum', full_name='context.ConfigActionEnum', @@ -235,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4428, - serialized_end=4521, + serialized_start=5174, + serialized_end=5267, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -262,6 +303,11 @@ SERVICESTATUS_UNDEFINED = 0 SERVICESTATUS_PLANNED = 1 SERVICESTATUS_ACTIVE = 2 SERVICESTATUS_PENDING_REMOVAL = 3 +SLICESTATUS_UNDEFINED = 0 +SLICESTATUS_PLANNED = 1 +SLICESTATUS_INIT = 2 +SLICESTATUS_ACTIVE = 3 +SLICESTATUS_DEINIT = 4 CONFIGACTION_UNDEFINED = 0 CONFIGACTION_SET = 1 CONFIGACTION_DELETE = 2 @@ -1421,6 +1467,247 @@ _SERVICEEVENT = _descriptor.Descriptor( ) +_SLICEID = _descriptor.Descriptor( + name='SliceId', + full_name='context.SliceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.SliceId.context_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='slice_uuid', full_name='context.SliceId.slice_uuid', 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=2604, + serialized_end=2688, +) + + +_SLICE = _descriptor.Descriptor( + name='Slice', + full_name='context.Slice', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_id', full_name='context.Slice.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='slice_endpoint_ids', full_name='context.Slice.slice_endpoint_ids', 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='slice_constraints', full_name='context.Slice.slice_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='slice_services', full_name='context.Slice.slice_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='slice_subslice_ids', full_name='context.Slice.slice_subslice_ids', 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='slice_status', full_name='context.Slice.slice_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=2691, + serialized_end=2965, +) + + +_SLICESTATUS = _descriptor.Descriptor( + name='SliceStatus', + full_name='context.SliceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_status', full_name='context.SliceStatus.slice_status', index=0, + number=1, 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=2967, + serialized_end=3028, +) + + +_SLICEIDLIST = _descriptor.Descriptor( + name='SliceIdList', + full_name='context.SliceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_ids', full_name='context.SliceIdList.slice_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3030, + serialized_end=3080, +) + + +_SLICELIST = _descriptor.Descriptor( + name='SliceList', + full_name='context.SliceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slices', full_name='context.SliceList.slices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3082, + serialized_end=3125, +) + + +_SLICEEVENT = _descriptor.Descriptor( + name='SliceEvent', + full_name='context.SliceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.SliceEvent.event', 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='context.SliceEvent.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=3127, + serialized_end=3206, +) + + _CONNECTIONID = _descriptor.Descriptor( name='ConnectionId', full_name='context.ConnectionId', @@ -1448,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2604, - serialized_end=2658, + serialized_start=3208, + serialized_end=3262, ) @@ -1501,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2661, - serialized_end=2857, + serialized_start=3265, + serialized_end=3461, ) @@ -1533,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2859, - serialized_end=2924, + serialized_start=3463, + serialized_end=3528, ) @@ -1565,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2926, - serialized_end=2984, + serialized_start=3530, + serialized_end=3588, ) @@ -1604,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2986, - serialized_end=3080, + serialized_start=3590, + serialized_end=3684, ) @@ -1650,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3083, - serialized_end=3213, + serialized_start=3687, + serialized_end=3817, ) @@ -1696,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3216, - serialized_end=3350, + serialized_start=3820, + serialized_end=3954, ) @@ -1742,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3352, - serialized_end=3453, + serialized_start=3956, + serialized_end=4057, ) @@ -1781,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3455, - serialized_end=3518, + serialized_start=4059, + serialized_end=4122, ) @@ -1827,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3520, - serialized_end=3614, + serialized_start=4124, + serialized_end=4218, ) @@ -1866,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3616, - serialized_end=3701, + serialized_start=4220, + serialized_end=4305, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -1921,6 +2208,19 @@ _SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID _SERVICELIST.fields_by_name['services'].message_type = _SERVICE _SERVICEEVENT.fields_by_name['event'].message_type = _EVENT _SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_SLICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SLICEID.fields_by_name['slice_uuid'].message_type = _UUID +_SLICE.fields_by_name['slice_id'].message_type = _SLICEID +_SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID +_SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT +_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID +_SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS +_SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM +_SLICEIDLIST.fields_by_name['slice_ids'].message_type = _SLICEID +_SLICELIST.fields_by_name['slices'].message_type = _SLICE +_SLICEEVENT.fields_by_name['event'].message_type = _EVENT +_SLICEEVENT.fields_by_name['slice_id'].message_type = _SLICEID _CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID _CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID _CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID @@ -1969,6 +2269,12 @@ DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['SliceId'] = _SLICEID +DESCRIPTOR.message_types_by_name['Slice'] = _SLICE +DESCRIPTOR.message_types_by_name['SliceStatus'] = _SLICESTATUS +DESCRIPTOR.message_types_by_name['SliceIdList'] = _SLICEIDLIST +DESCRIPTOR.message_types_by_name['SliceList'] = _SLICELIST +DESCRIPTOR.message_types_by_name['SliceEvent'] = _SLICEEVENT DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST @@ -1985,6 +2291,7 @@ DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['SliceStatusEnum'] = _SLICESTATUSENUM DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -2205,6 +2512,48 @@ ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_messag }) _sym_db.RegisterMessage(ServiceEvent) +SliceId = _reflection.GeneratedProtocolMessageType('SliceId', (_message.Message,), { + 'DESCRIPTOR' : _SLICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceId) + }) +_sym_db.RegisterMessage(SliceId) + +Slice = _reflection.GeneratedProtocolMessageType('Slice', (_message.Message,), { + 'DESCRIPTOR' : _SLICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Slice) + }) +_sym_db.RegisterMessage(Slice) + +SliceStatus = _reflection.GeneratedProtocolMessageType('SliceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SLICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceStatus) + }) +_sym_db.RegisterMessage(SliceStatus) + +SliceIdList = _reflection.GeneratedProtocolMessageType('SliceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SLICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceIdList) + }) +_sym_db.RegisterMessage(SliceIdList) + +SliceList = _reflection.GeneratedProtocolMessageType('SliceList', (_message.Message,), { + 'DESCRIPTOR' : _SLICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceList) + }) +_sym_db.RegisterMessage(SliceList) + +SliceEvent = _reflection.GeneratedProtocolMessageType('SliceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SLICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceEvent) + }) +_sym_db.RegisterMessage(SliceEvent) + ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { 'DESCRIPTOR' : _CONNECTIONID, '__module__' : 'context_pb2' @@ -2291,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=4524, - serialized_end=6617, + serialized_start=5270, + serialized_end=7363, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/interdomain/proto/context_pb2.py b/src/interdomain/proto/context_pb2.py index 68602b16f..3e1b4c54d 100644 --- a/src/interdomain/proto/context_pb2.py +++ b/src/interdomain/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3703, - serialized_end=3809, + serialized_start=4307, + serialized_end=4413, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3812, - serialized_end=4009, + serialized_start=4416, + serialized_end=4613, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4012, - serialized_end=4155, + serialized_start=4616, + serialized_end=4759, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4158, - serialized_end=4287, + serialized_start=4762, + serialized_end=4891, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,12 +204,53 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4290, - serialized_end=4426, + serialized_start=4894, + serialized_end=5030, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_SLICESTATUSENUM = _descriptor.EnumDescriptor( + name='SliceStatusEnum', + full_name='context.SliceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_INIT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_ACTIVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_DEINIT', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5033, + serialized_end=5172, +) +_sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) + +SliceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SLICESTATUSENUM) _CONFIGACTIONENUM = _descriptor.EnumDescriptor( name='ConfigActionEnum', full_name='context.ConfigActionEnum', @@ -235,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4428, - serialized_end=4521, + serialized_start=5174, + serialized_end=5267, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -262,6 +303,11 @@ SERVICESTATUS_UNDEFINED = 0 SERVICESTATUS_PLANNED = 1 SERVICESTATUS_ACTIVE = 2 SERVICESTATUS_PENDING_REMOVAL = 3 +SLICESTATUS_UNDEFINED = 0 +SLICESTATUS_PLANNED = 1 +SLICESTATUS_INIT = 2 +SLICESTATUS_ACTIVE = 3 +SLICESTATUS_DEINIT = 4 CONFIGACTION_UNDEFINED = 0 CONFIGACTION_SET = 1 CONFIGACTION_DELETE = 2 @@ -1421,6 +1467,247 @@ _SERVICEEVENT = _descriptor.Descriptor( ) +_SLICEID = _descriptor.Descriptor( + name='SliceId', + full_name='context.SliceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.SliceId.context_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='slice_uuid', full_name='context.SliceId.slice_uuid', 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=2604, + serialized_end=2688, +) + + +_SLICE = _descriptor.Descriptor( + name='Slice', + full_name='context.Slice', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_id', full_name='context.Slice.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='slice_endpoint_ids', full_name='context.Slice.slice_endpoint_ids', 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='slice_constraints', full_name='context.Slice.slice_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='slice_services', full_name='context.Slice.slice_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='slice_subslice_ids', full_name='context.Slice.slice_subslice_ids', 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='slice_status', full_name='context.Slice.slice_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=2691, + serialized_end=2965, +) + + +_SLICESTATUS = _descriptor.Descriptor( + name='SliceStatus', + full_name='context.SliceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_status', full_name='context.SliceStatus.slice_status', index=0, + number=1, 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=2967, + serialized_end=3028, +) + + +_SLICEIDLIST = _descriptor.Descriptor( + name='SliceIdList', + full_name='context.SliceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_ids', full_name='context.SliceIdList.slice_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3030, + serialized_end=3080, +) + + +_SLICELIST = _descriptor.Descriptor( + name='SliceList', + full_name='context.SliceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slices', full_name='context.SliceList.slices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3082, + serialized_end=3125, +) + + +_SLICEEVENT = _descriptor.Descriptor( + name='SliceEvent', + full_name='context.SliceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.SliceEvent.event', 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='context.SliceEvent.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=3127, + serialized_end=3206, +) + + _CONNECTIONID = _descriptor.Descriptor( name='ConnectionId', full_name='context.ConnectionId', @@ -1448,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2604, - serialized_end=2658, + serialized_start=3208, + serialized_end=3262, ) @@ -1501,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2661, - serialized_end=2857, + serialized_start=3265, + serialized_end=3461, ) @@ -1533,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2859, - serialized_end=2924, + serialized_start=3463, + serialized_end=3528, ) @@ -1565,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2926, - serialized_end=2984, + serialized_start=3530, + serialized_end=3588, ) @@ -1604,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2986, - serialized_end=3080, + serialized_start=3590, + serialized_end=3684, ) @@ -1650,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3083, - serialized_end=3213, + serialized_start=3687, + serialized_end=3817, ) @@ -1696,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3216, - serialized_end=3350, + serialized_start=3820, + serialized_end=3954, ) @@ -1742,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3352, - serialized_end=3453, + serialized_start=3956, + serialized_end=4057, ) @@ -1781,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3455, - serialized_end=3518, + serialized_start=4059, + serialized_end=4122, ) @@ -1827,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3520, - serialized_end=3614, + serialized_start=4124, + serialized_end=4218, ) @@ -1866,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3616, - serialized_end=3701, + serialized_start=4220, + serialized_end=4305, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -1921,6 +2208,19 @@ _SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID _SERVICELIST.fields_by_name['services'].message_type = _SERVICE _SERVICEEVENT.fields_by_name['event'].message_type = _EVENT _SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_SLICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SLICEID.fields_by_name['slice_uuid'].message_type = _UUID +_SLICE.fields_by_name['slice_id'].message_type = _SLICEID +_SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID +_SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT +_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID +_SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS +_SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM +_SLICEIDLIST.fields_by_name['slice_ids'].message_type = _SLICEID +_SLICELIST.fields_by_name['slices'].message_type = _SLICE +_SLICEEVENT.fields_by_name['event'].message_type = _EVENT +_SLICEEVENT.fields_by_name['slice_id'].message_type = _SLICEID _CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID _CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID _CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID @@ -1969,6 +2269,12 @@ DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['SliceId'] = _SLICEID +DESCRIPTOR.message_types_by_name['Slice'] = _SLICE +DESCRIPTOR.message_types_by_name['SliceStatus'] = _SLICESTATUS +DESCRIPTOR.message_types_by_name['SliceIdList'] = _SLICEIDLIST +DESCRIPTOR.message_types_by_name['SliceList'] = _SLICELIST +DESCRIPTOR.message_types_by_name['SliceEvent'] = _SLICEEVENT DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST @@ -1985,6 +2291,7 @@ DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['SliceStatusEnum'] = _SLICESTATUSENUM DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -2205,6 +2512,48 @@ ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_messag }) _sym_db.RegisterMessage(ServiceEvent) +SliceId = _reflection.GeneratedProtocolMessageType('SliceId', (_message.Message,), { + 'DESCRIPTOR' : _SLICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceId) + }) +_sym_db.RegisterMessage(SliceId) + +Slice = _reflection.GeneratedProtocolMessageType('Slice', (_message.Message,), { + 'DESCRIPTOR' : _SLICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Slice) + }) +_sym_db.RegisterMessage(Slice) + +SliceStatus = _reflection.GeneratedProtocolMessageType('SliceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SLICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceStatus) + }) +_sym_db.RegisterMessage(SliceStatus) + +SliceIdList = _reflection.GeneratedProtocolMessageType('SliceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SLICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceIdList) + }) +_sym_db.RegisterMessage(SliceIdList) + +SliceList = _reflection.GeneratedProtocolMessageType('SliceList', (_message.Message,), { + 'DESCRIPTOR' : _SLICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceList) + }) +_sym_db.RegisterMessage(SliceList) + +SliceEvent = _reflection.GeneratedProtocolMessageType('SliceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SLICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceEvent) + }) +_sym_db.RegisterMessage(SliceEvent) + ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { 'DESCRIPTOR' : _CONNECTIONID, '__module__' : 'context_pb2' @@ -2291,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=4524, - serialized_end=6617, + serialized_start=5270, + serialized_end=7363, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/interdomain/proto/interdomain_pb2.py b/src/interdomain/proto/interdomain_pb2.py index 28abd7785..9745bafeb 100644 --- a/src/interdomain/proto/interdomain_pb2.py +++ b/src/interdomain/proto/interdomain_pb2.py @@ -12,7 +12,6 @@ _sym_db = _symbol_database.Default() from . import context_pb2 as context__pb2 -from . import slice_pb2 as slice__pb2 DESCRIPTOR = _descriptor.FileDescriptor( @@ -21,9 +20,9 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x11interdomain.proto\x12\x0binterdomain\x1a\rcontext.proto\x1a\x0bslice.proto2\xab\x02\n\x12InterdomainService\x12L\n\x0c\x41uthenticate\x12\x1b.context.TeraFlowController\x1a\x1d.context.AuthenticationResult\"\x00\x12\x36\n\x0bLookUpSlice\x12\x15.slice.TransportSlice\x1a\x0e.slice.SliceId\"\x00\x12\x44\n\x15OrderSliceFromCatalog\x12\x15.slice.TransportSlice\x1a\x12.slice.SliceStatus\"\x00\x12I\n\x1a\x43reateSliceAndAddToCatalog\x12\x15.slice.TransportSlice\x1a\x12.slice.SliceStatus\"\x00\x62\x06proto3' + serialized_pb=b'\n\x11interdomain.proto\x12\x0binterdomain\x1a\rcontext.proto2\x90\x02\n\x12InterdomainService\x12L\n\x0c\x41uthenticate\x12\x1b.context.TeraFlowController\x1a\x1d.context.AuthenticationResult\"\x00\x12\x31\n\x0bLookUpSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x39\n\x15OrderSliceFromCatalog\x12\x0e.context.Slice\x1a\x0e.context.Slice\"\x00\x12>\n\x1a\x43reateSliceAndAddToCatalog\x12\x0e.context.Slice\x1a\x0e.context.Slice\"\x00\x62\x06proto3' , - dependencies=[context__pb2.DESCRIPTOR,slice__pb2.DESCRIPTOR,]) + dependencies=[context__pb2.DESCRIPTOR,]) @@ -38,8 +37,8 @@ _INTERDOMAINSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=63, - serialized_end=362, + serialized_start=50, + serialized_end=322, methods=[ _descriptor.MethodDescriptor( name='Authenticate', @@ -56,8 +55,8 @@ _INTERDOMAINSERVICE = _descriptor.ServiceDescriptor( full_name='interdomain.InterdomainService.LookUpSlice', index=1, containing_service=None, - input_type=slice__pb2._TRANSPORTSLICE, - output_type=slice__pb2._SLICEID, + input_type=context__pb2._SLICE, + output_type=context__pb2._SLICEID, serialized_options=None, create_key=_descriptor._internal_create_key, ), @@ -66,8 +65,8 @@ _INTERDOMAINSERVICE = _descriptor.ServiceDescriptor( full_name='interdomain.InterdomainService.OrderSliceFromCatalog', index=2, containing_service=None, - input_type=slice__pb2._TRANSPORTSLICE, - output_type=slice__pb2._SLICESTATUS, + input_type=context__pb2._SLICE, + output_type=context__pb2._SLICE, serialized_options=None, create_key=_descriptor._internal_create_key, ), @@ -76,8 +75,8 @@ _INTERDOMAINSERVICE = _descriptor.ServiceDescriptor( full_name='interdomain.InterdomainService.CreateSliceAndAddToCatalog', index=3, containing_service=None, - input_type=slice__pb2._TRANSPORTSLICE, - output_type=slice__pb2._SLICESTATUS, + input_type=context__pb2._SLICE, + output_type=context__pb2._SLICE, serialized_options=None, create_key=_descriptor._internal_create_key, ), diff --git a/src/interdomain/proto/interdomain_pb2_grpc.py b/src/interdomain/proto/interdomain_pb2_grpc.py index b415f4177..60f142859 100644 --- a/src/interdomain/proto/interdomain_pb2_grpc.py +++ b/src/interdomain/proto/interdomain_pb2_grpc.py @@ -3,7 +3,6 @@ import grpc from . import context_pb2 as context__pb2 -from . import slice_pb2 as slice__pb2 class InterdomainServiceStub(object): @@ -22,18 +21,18 @@ class InterdomainServiceStub(object): ) self.LookUpSlice = channel.unary_unary( '/interdomain.InterdomainService/LookUpSlice', - request_serializer=slice__pb2.TransportSlice.SerializeToString, - response_deserializer=slice__pb2.SliceId.FromString, + request_serializer=context__pb2.Slice.SerializeToString, + response_deserializer=context__pb2.SliceId.FromString, ) self.OrderSliceFromCatalog = channel.unary_unary( '/interdomain.InterdomainService/OrderSliceFromCatalog', - request_serializer=slice__pb2.TransportSlice.SerializeToString, - response_deserializer=slice__pb2.SliceStatus.FromString, + request_serializer=context__pb2.Slice.SerializeToString, + response_deserializer=context__pb2.Slice.FromString, ) self.CreateSliceAndAddToCatalog = channel.unary_unary( '/interdomain.InterdomainService/CreateSliceAndAddToCatalog', - request_serializer=slice__pb2.TransportSlice.SerializeToString, - response_deserializer=slice__pb2.SliceStatus.FromString, + request_serializer=context__pb2.Slice.SerializeToString, + response_deserializer=context__pb2.Slice.FromString, ) @@ -74,18 +73,18 @@ def add_InterdomainServiceServicer_to_server(servicer, server): ), 'LookUpSlice': grpc.unary_unary_rpc_method_handler( servicer.LookUpSlice, - request_deserializer=slice__pb2.TransportSlice.FromString, - response_serializer=slice__pb2.SliceId.SerializeToString, + request_deserializer=context__pb2.Slice.FromString, + response_serializer=context__pb2.SliceId.SerializeToString, ), 'OrderSliceFromCatalog': grpc.unary_unary_rpc_method_handler( servicer.OrderSliceFromCatalog, - request_deserializer=slice__pb2.TransportSlice.FromString, - response_serializer=slice__pb2.SliceStatus.SerializeToString, + request_deserializer=context__pb2.Slice.FromString, + response_serializer=context__pb2.Slice.SerializeToString, ), 'CreateSliceAndAddToCatalog': grpc.unary_unary_rpc_method_handler( servicer.CreateSliceAndAddToCatalog, - request_deserializer=slice__pb2.TransportSlice.FromString, - response_serializer=slice__pb2.SliceStatus.SerializeToString, + request_deserializer=context__pb2.Slice.FromString, + response_serializer=context__pb2.Slice.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( @@ -126,8 +125,8 @@ class InterdomainService(object): timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/interdomain.InterdomainService/LookUpSlice', - slice__pb2.TransportSlice.SerializeToString, - slice__pb2.SliceId.FromString, + context__pb2.Slice.SerializeToString, + context__pb2.SliceId.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @@ -143,8 +142,8 @@ class InterdomainService(object): timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/interdomain.InterdomainService/OrderSliceFromCatalog', - slice__pb2.TransportSlice.SerializeToString, - slice__pb2.SliceStatus.FromString, + context__pb2.Slice.SerializeToString, + context__pb2.Slice.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @@ -160,7 +159,7 @@ class InterdomainService(object): timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/interdomain.InterdomainService/CreateSliceAndAddToCatalog', - slice__pb2.TransportSlice.SerializeToString, - slice__pb2.SliceStatus.FromString, + context__pb2.Slice.SerializeToString, + context__pb2.Slice.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/interdomain/proto/slice_pb2.py b/src/interdomain/proto/slice_pb2.py index 91dbaaae3..ac8b165db 100644 --- a/src/interdomain/proto/slice_pb2.py +++ b/src/interdomain/proto/slice_pb2.py @@ -2,7 +2,6 @@ # 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 @@ -21,275 +20,14 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x0bslice.proto\x12\x05slice\x1a\rcontext.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.context.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' + serialized_pb=b'\n\x0bslice.proto\x12\x05slice\x1a\rcontext.proto2x\n\x0cSliceService\x12\x37\n\x11\x43reateUpdateSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12/\n\x0b\x44\x65leteSlice\x12\x0e.context.Slice\x1a\x0e.context.Empty\"\x00\x62\x06proto3' , dependencies=[context__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=509, - serialized_end=573, -) -_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=37, - serialized_end=88, -) - - -_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=91, - serialized_end=335, -) -_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=337, - serialized_end=418, -) - - -_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=420, - serialized_end=507, -) - -_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 = context__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( @@ -299,16 +37,16 @@ _SLICESERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=576, - serialized_end=712, + serialized_start=37, + serialized_end=157, methods=[ _descriptor.MethodDescriptor( name='CreateUpdateSlice', full_name='slice.SliceService.CreateUpdateSlice', index=0, containing_service=None, - input_type=_TRANSPORTSLICE, - output_type=_SLICESTATUS, + input_type=context__pb2._SLICE, + output_type=context__pb2._SLICEID, serialized_options=None, create_key=_descriptor._internal_create_key, ), @@ -317,7 +55,7 @@ _SLICESERVICE = _descriptor.ServiceDescriptor( full_name='slice.SliceService.DeleteSlice', index=1, containing_service=None, - input_type=_TRANSPORTSLICE, + input_type=context__pb2._SLICE, output_type=context__pb2._EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, diff --git a/src/l3_attackmitigator/genproto.sh b/src/l3_attackmitigator/genproto.sh old mode 100644 new mode 100755 diff --git a/src/l3_attackmitigator/proto/__init__.py b/src/l3_attackmitigator/proto/__init__.py index 70a332512..e69de29bb 100644 --- a/src/l3_attackmitigator/proto/__init__.py +++ b/src/l3_attackmitigator/proto/__init__.py @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/l3_attackmitigator/proto/l3_attackmitigator_pb2.py b/src/l3_attackmitigator/proto/l3_attackmitigator_pb2.py index 0ad49c2ed..e148d0a2c 100644 --- a/src/l3_attackmitigator/proto/l3_attackmitigator_pb2.py +++ b/src/l3_attackmitigator/proto/l3_attackmitigator_pb2.py @@ -11,6 +11,7 @@ from google.protobuf import symbol_database as _symbol_database _sym_db = _symbol_database.Default() +from . import context_pb2 as context__pb2 DESCRIPTOR = _descriptor.FileDescriptor( @@ -19,129 +20,93 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x18l3_attackmitigator.proto\"2\n\x0e\x45mptyMitigator\x12\x14\n\x07message\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_message\"\xf9\x01\n\x06Output\x12\x12\n\nconfidence\x18\x01 \x01(\x02\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\x0c\n\x04ip_o\x18\x03 \x01(\t\x12\x10\n\x08tag_name\x18\x04 \x01(\t\x12\x0b\n\x03tag\x18\x05 \x01(\x05\x12\x0f\n\x07\x66low_id\x18\x06 \x01(\t\x12\x10\n\x08protocol\x18\x07 \x01(\t\x12\x0e\n\x06port_d\x18\x08 \x01(\t\x12\x12\n\x05ml_id\x18\t \x01(\tH\x00\x88\x01\x01\x12\x17\n\ntime_start\x18\n \x01(\x02H\x01\x88\x01\x01\x12\x15\n\x08time_end\x18\x0b \x01(\x02H\x02\x88\x01\x01\x42\x08\n\x06_ml_idB\r\n\x0b_time_startB\x0b\n\t_time_end2r\n\x11L3Attackmitigator\x12(\n\nSendOutput\x12\x07.Output\x1a\x0f.EmptyMitigator\"\x00\x12\x33\n\rGetMitigation\x12\x0f.EmptyMitigator\x1a\x0f.EmptyMitigator\"\x00\x62\x06proto3' -) + serialized_pb=b'\n\x18l3_attackmitigator.proto\x1a\rcontext.proto\"\xd5\x01\n\x17L3AttackmitigatorOutput\x12\x12\n\nconfidence\x18\x01 \x01(\x02\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\x0c\n\x04ip_o\x18\x03 \x01(\t\x12\x10\n\x08tag_name\x18\x04 \x01(\t\x12\x0b\n\x03tag\x18\x05 \x01(\x05\x12\x0f\n\x07\x66low_id\x18\x06 \x01(\t\x12\x10\n\x08protocol\x18\x07 \x01(\t\x12\x0e\n\x06port_d\x18\x08 \x01(\t\x12\r\n\x05ml_id\x18\t \x01(\t\x12\x12\n\ntime_start\x18\n \x01(\x02\x12\x10\n\x08time_end\x18\x0b \x01(\x02\x32\x80\x01\n\x11L3Attackmitigator\x12\x38\n\nSendOutput\x12\x18.L3AttackmitigatorOutput\x1a\x0e.context.Empty\"\x00\x12\x31\n\rGetMitigation\x12\x0e.context.Empty\x1a\x0e.context.Empty\"\x00\x62\x06proto3' + , + dependencies=[context__pb2.DESCRIPTOR,]) -_EMPTYMITIGATOR = _descriptor.Descriptor( - name='EmptyMitigator', - full_name='EmptyMitigator', +_L3ATTACKMITIGATOROUTPUT = _descriptor.Descriptor( + name='L3AttackmitigatorOutput', + full_name='L3AttackmitigatorOutput', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='message', full_name='EmptyMitigator.message', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - 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=[ - _descriptor.OneofDescriptor( - name='_message', full_name='EmptyMitigator._message', - index=0, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), - ], - serialized_start=28, - serialized_end=78, -) - - -_OUTPUT = _descriptor.Descriptor( - name='Output', - full_name='Output', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='confidence', full_name='Output.confidence', index=0, + name='confidence', full_name='L3AttackmitigatorOutput.confidence', index=0, number=1, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='timestamp', full_name='Output.timestamp', index=1, + name='timestamp', full_name='L3AttackmitigatorOutput.timestamp', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='ip_o', full_name='Output.ip_o', index=2, + name='ip_o', full_name='L3AttackmitigatorOutput.ip_o', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='tag_name', full_name='Output.tag_name', index=3, + name='tag_name', full_name='L3AttackmitigatorOutput.tag_name', index=3, number=4, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='tag', full_name='Output.tag', index=4, + name='tag', full_name='L3AttackmitigatorOutput.tag', index=4, number=5, type=5, cpp_type=1, 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), _descriptor.FieldDescriptor( - name='flow_id', full_name='Output.flow_id', index=5, + name='flow_id', full_name='L3AttackmitigatorOutput.flow_id', index=5, number=6, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='protocol', full_name='Output.protocol', index=6, + name='protocol', full_name='L3AttackmitigatorOutput.protocol', index=6, number=7, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='port_d', full_name='Output.port_d', index=7, + name='port_d', full_name='L3AttackmitigatorOutput.port_d', index=7, number=8, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='ml_id', full_name='Output.ml_id', index=8, + name='ml_id', full_name='L3AttackmitigatorOutput.ml_id', index=8, number=9, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='time_start', full_name='Output.time_start', index=9, + name='time_start', full_name='L3AttackmitigatorOutput.time_start', index=9, number=10, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='time_end', full_name='Output.time_end', index=10, + name='time_end', full_name='L3AttackmitigatorOutput.time_end', index=10, number=11, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(0), message_type=None, enum_type=None, containing_type=None, @@ -158,55 +123,20 @@ _OUTPUT = _descriptor.Descriptor( syntax='proto3', extension_ranges=[], oneofs=[ - _descriptor.OneofDescriptor( - name='_ml_id', full_name='Output._ml_id', - index=0, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), - _descriptor.OneofDescriptor( - name='_time_start', full_name='Output._time_start', - index=1, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), - _descriptor.OneofDescriptor( - name='_time_end', full_name='Output._time_end', - index=2, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), ], - serialized_start=81, - serialized_end=330, + serialized_start=44, + serialized_end=257, ) -_EMPTYMITIGATOR.oneofs_by_name['_message'].fields.append( - _EMPTYMITIGATOR.fields_by_name['message']) -_EMPTYMITIGATOR.fields_by_name['message'].containing_oneof = _EMPTYMITIGATOR.oneofs_by_name['_message'] -_OUTPUT.oneofs_by_name['_ml_id'].fields.append( - _OUTPUT.fields_by_name['ml_id']) -_OUTPUT.fields_by_name['ml_id'].containing_oneof = _OUTPUT.oneofs_by_name['_ml_id'] -_OUTPUT.oneofs_by_name['_time_start'].fields.append( - _OUTPUT.fields_by_name['time_start']) -_OUTPUT.fields_by_name['time_start'].containing_oneof = _OUTPUT.oneofs_by_name['_time_start'] -_OUTPUT.oneofs_by_name['_time_end'].fields.append( - _OUTPUT.fields_by_name['time_end']) -_OUTPUT.fields_by_name['time_end'].containing_oneof = _OUTPUT.oneofs_by_name['_time_end'] -DESCRIPTOR.message_types_by_name['EmptyMitigator'] = _EMPTYMITIGATOR -DESCRIPTOR.message_types_by_name['Output'] = _OUTPUT +DESCRIPTOR.message_types_by_name['L3AttackmitigatorOutput'] = _L3ATTACKMITIGATOROUTPUT _sym_db.RegisterFileDescriptor(DESCRIPTOR) -EmptyMitigator = _reflection.GeneratedProtocolMessageType('EmptyMitigator', (_message.Message,), { - 'DESCRIPTOR' : _EMPTYMITIGATOR, - '__module__' : 'l3_attackmitigator_pb2' - # @@protoc_insertion_point(class_scope:EmptyMitigator) - }) -_sym_db.RegisterMessage(EmptyMitigator) - -Output = _reflection.GeneratedProtocolMessageType('Output', (_message.Message,), { - 'DESCRIPTOR' : _OUTPUT, +L3AttackmitigatorOutput = _reflection.GeneratedProtocolMessageType('L3AttackmitigatorOutput', (_message.Message,), { + 'DESCRIPTOR' : _L3ATTACKMITIGATOROUTPUT, '__module__' : 'l3_attackmitigator_pb2' - # @@protoc_insertion_point(class_scope:Output) + # @@protoc_insertion_point(class_scope:L3AttackmitigatorOutput) }) -_sym_db.RegisterMessage(Output) +_sym_db.RegisterMessage(L3AttackmitigatorOutput) @@ -217,16 +147,16 @@ _L3ATTACKMITIGATOR = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=332, - serialized_end=446, + serialized_start=260, + serialized_end=388, methods=[ _descriptor.MethodDescriptor( name='SendOutput', full_name='L3Attackmitigator.SendOutput', index=0, containing_service=None, - input_type=_OUTPUT, - output_type=_EMPTYMITIGATOR, + input_type=_L3ATTACKMITIGATOROUTPUT, + output_type=context__pb2._EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, ), @@ -235,8 +165,8 @@ _L3ATTACKMITIGATOR = _descriptor.ServiceDescriptor( full_name='L3Attackmitigator.GetMitigation', index=1, containing_service=None, - input_type=_EMPTYMITIGATOR, - output_type=_EMPTYMITIGATOR, + input_type=context__pb2._EMPTY, + output_type=context__pb2._EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, ), diff --git a/src/l3_attackmitigator/proto/l3_attackmitigator_pb2_grpc.py b/src/l3_attackmitigator/proto/l3_attackmitigator_pb2_grpc.py index 3942d6843..25d4afdba 100644 --- a/src/l3_attackmitigator/proto/l3_attackmitigator_pb2_grpc.py +++ b/src/l3_attackmitigator/proto/l3_attackmitigator_pb2_grpc.py @@ -2,7 +2,7 @@ """Client and server classes corresponding to protobuf-defined services.""" import grpc - +from . import context_pb2 as context__pb2 from . import l3_attackmitigator_pb2 as l3__attackmitigator__pb2 @@ -17,13 +17,13 @@ class L3AttackmitigatorStub(object): """ self.SendOutput = channel.unary_unary( '/L3Attackmitigator/SendOutput', - request_serializer=l3__attackmitigator__pb2.Output.SerializeToString, - response_deserializer=l3__attackmitigator__pb2.EmptyMitigator.FromString, + request_serializer=l3__attackmitigator__pb2.L3AttackmitigatorOutput.SerializeToString, + response_deserializer=context__pb2.Empty.FromString, ) self.GetMitigation = channel.unary_unary( '/L3Attackmitigator/GetMitigation', - request_serializer=l3__attackmitigator__pb2.EmptyMitigator.SerializeToString, - response_deserializer=l3__attackmitigator__pb2.EmptyMitigator.FromString, + request_serializer=context__pb2.Empty.SerializeToString, + response_deserializer=context__pb2.Empty.FromString, ) @@ -49,13 +49,13 @@ def add_L3AttackmitigatorServicer_to_server(servicer, server): rpc_method_handlers = { 'SendOutput': grpc.unary_unary_rpc_method_handler( servicer.SendOutput, - request_deserializer=l3__attackmitigator__pb2.Output.FromString, - response_serializer=l3__attackmitigator__pb2.EmptyMitigator.SerializeToString, + request_deserializer=l3__attackmitigator__pb2.L3AttackmitigatorOutput.FromString, + response_serializer=context__pb2.Empty.SerializeToString, ), 'GetMitigation': grpc.unary_unary_rpc_method_handler( servicer.GetMitigation, - request_deserializer=l3__attackmitigator__pb2.EmptyMitigator.FromString, - response_serializer=l3__attackmitigator__pb2.EmptyMitigator.SerializeToString, + request_deserializer=context__pb2.Empty.FromString, + response_serializer=context__pb2.Empty.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( @@ -79,8 +79,8 @@ class L3Attackmitigator(object): timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/L3Attackmitigator/SendOutput', - l3__attackmitigator__pb2.Output.SerializeToString, - l3__attackmitigator__pb2.EmptyMitigator.FromString, + l3__attackmitigator__pb2.L3AttackmitigatorOutput.SerializeToString, + context__pb2.Empty.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @@ -96,7 +96,7 @@ class L3Attackmitigator(object): timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/L3Attackmitigator/GetMitigation', - l3__attackmitigator__pb2.EmptyMitigator.SerializeToString, - l3__attackmitigator__pb2.EmptyMitigator.FromString, + context__pb2.Empty.SerializeToString, + context__pb2.Empty.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/l3_centralizedattackdetector/genproto.sh b/src/l3_centralizedattackdetector/genproto.sh old mode 100644 new mode 100755 diff --git a/src/l3_centralizedattackdetector/proto/__init__.py b/src/l3_centralizedattackdetector/proto/__init__.py index 70a332512..e69de29bb 100644 --- a/src/l3_centralizedattackdetector/proto/__init__.py +++ b/src/l3_centralizedattackdetector/proto/__init__.py @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/l3_centralizedattackdetector/proto/l3_attackmitigator_pb2.py b/src/l3_centralizedattackdetector/proto/l3_attackmitigator_pb2.py index 0ad49c2ed..e148d0a2c 100644 --- a/src/l3_centralizedattackdetector/proto/l3_attackmitigator_pb2.py +++ b/src/l3_centralizedattackdetector/proto/l3_attackmitigator_pb2.py @@ -11,6 +11,7 @@ from google.protobuf import symbol_database as _symbol_database _sym_db = _symbol_database.Default() +from . import context_pb2 as context__pb2 DESCRIPTOR = _descriptor.FileDescriptor( @@ -19,129 +20,93 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x18l3_attackmitigator.proto\"2\n\x0e\x45mptyMitigator\x12\x14\n\x07message\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_message\"\xf9\x01\n\x06Output\x12\x12\n\nconfidence\x18\x01 \x01(\x02\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\x0c\n\x04ip_o\x18\x03 \x01(\t\x12\x10\n\x08tag_name\x18\x04 \x01(\t\x12\x0b\n\x03tag\x18\x05 \x01(\x05\x12\x0f\n\x07\x66low_id\x18\x06 \x01(\t\x12\x10\n\x08protocol\x18\x07 \x01(\t\x12\x0e\n\x06port_d\x18\x08 \x01(\t\x12\x12\n\x05ml_id\x18\t \x01(\tH\x00\x88\x01\x01\x12\x17\n\ntime_start\x18\n \x01(\x02H\x01\x88\x01\x01\x12\x15\n\x08time_end\x18\x0b \x01(\x02H\x02\x88\x01\x01\x42\x08\n\x06_ml_idB\r\n\x0b_time_startB\x0b\n\t_time_end2r\n\x11L3Attackmitigator\x12(\n\nSendOutput\x12\x07.Output\x1a\x0f.EmptyMitigator\"\x00\x12\x33\n\rGetMitigation\x12\x0f.EmptyMitigator\x1a\x0f.EmptyMitigator\"\x00\x62\x06proto3' -) + serialized_pb=b'\n\x18l3_attackmitigator.proto\x1a\rcontext.proto\"\xd5\x01\n\x17L3AttackmitigatorOutput\x12\x12\n\nconfidence\x18\x01 \x01(\x02\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\x0c\n\x04ip_o\x18\x03 \x01(\t\x12\x10\n\x08tag_name\x18\x04 \x01(\t\x12\x0b\n\x03tag\x18\x05 \x01(\x05\x12\x0f\n\x07\x66low_id\x18\x06 \x01(\t\x12\x10\n\x08protocol\x18\x07 \x01(\t\x12\x0e\n\x06port_d\x18\x08 \x01(\t\x12\r\n\x05ml_id\x18\t \x01(\t\x12\x12\n\ntime_start\x18\n \x01(\x02\x12\x10\n\x08time_end\x18\x0b \x01(\x02\x32\x80\x01\n\x11L3Attackmitigator\x12\x38\n\nSendOutput\x12\x18.L3AttackmitigatorOutput\x1a\x0e.context.Empty\"\x00\x12\x31\n\rGetMitigation\x12\x0e.context.Empty\x1a\x0e.context.Empty\"\x00\x62\x06proto3' + , + dependencies=[context__pb2.DESCRIPTOR,]) -_EMPTYMITIGATOR = _descriptor.Descriptor( - name='EmptyMitigator', - full_name='EmptyMitigator', +_L3ATTACKMITIGATOROUTPUT = _descriptor.Descriptor( + name='L3AttackmitigatorOutput', + full_name='L3AttackmitigatorOutput', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='message', full_name='EmptyMitigator.message', index=0, - number=1, type=9, cpp_type=9, label=1, - has_default_value=False, default_value=b"".decode('utf-8'), - 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=[ - _descriptor.OneofDescriptor( - name='_message', full_name='EmptyMitigator._message', - index=0, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), - ], - serialized_start=28, - serialized_end=78, -) - - -_OUTPUT = _descriptor.Descriptor( - name='Output', - full_name='Output', - filename=None, - file=DESCRIPTOR, - containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[ - _descriptor.FieldDescriptor( - name='confidence', full_name='Output.confidence', index=0, + name='confidence', full_name='L3AttackmitigatorOutput.confidence', index=0, number=1, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='timestamp', full_name='Output.timestamp', index=1, + name='timestamp', full_name='L3AttackmitigatorOutput.timestamp', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='ip_o', full_name='Output.ip_o', index=2, + name='ip_o', full_name='L3AttackmitigatorOutput.ip_o', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='tag_name', full_name='Output.tag_name', index=3, + name='tag_name', full_name='L3AttackmitigatorOutput.tag_name', index=3, number=4, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='tag', full_name='Output.tag', index=4, + name='tag', full_name='L3AttackmitigatorOutput.tag', index=4, number=5, type=5, cpp_type=1, 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), _descriptor.FieldDescriptor( - name='flow_id', full_name='Output.flow_id', index=5, + name='flow_id', full_name='L3AttackmitigatorOutput.flow_id', index=5, number=6, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='protocol', full_name='Output.protocol', index=6, + name='protocol', full_name='L3AttackmitigatorOutput.protocol', index=6, number=7, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='port_d', full_name='Output.port_d', index=7, + name='port_d', full_name='L3AttackmitigatorOutput.port_d', index=7, number=8, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='ml_id', full_name='Output.ml_id', index=8, + name='ml_id', full_name='L3AttackmitigatorOutput.ml_id', index=8, number=9, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='time_start', full_name='Output.time_start', index=9, + name='time_start', full_name='L3AttackmitigatorOutput.time_start', index=9, number=10, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='time_end', full_name='Output.time_end', index=10, + name='time_end', full_name='L3AttackmitigatorOutput.time_end', index=10, number=11, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(0), message_type=None, enum_type=None, containing_type=None, @@ -158,55 +123,20 @@ _OUTPUT = _descriptor.Descriptor( syntax='proto3', extension_ranges=[], oneofs=[ - _descriptor.OneofDescriptor( - name='_ml_id', full_name='Output._ml_id', - index=0, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), - _descriptor.OneofDescriptor( - name='_time_start', full_name='Output._time_start', - index=1, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), - _descriptor.OneofDescriptor( - name='_time_end', full_name='Output._time_end', - index=2, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), ], - serialized_start=81, - serialized_end=330, + serialized_start=44, + serialized_end=257, ) -_EMPTYMITIGATOR.oneofs_by_name['_message'].fields.append( - _EMPTYMITIGATOR.fields_by_name['message']) -_EMPTYMITIGATOR.fields_by_name['message'].containing_oneof = _EMPTYMITIGATOR.oneofs_by_name['_message'] -_OUTPUT.oneofs_by_name['_ml_id'].fields.append( - _OUTPUT.fields_by_name['ml_id']) -_OUTPUT.fields_by_name['ml_id'].containing_oneof = _OUTPUT.oneofs_by_name['_ml_id'] -_OUTPUT.oneofs_by_name['_time_start'].fields.append( - _OUTPUT.fields_by_name['time_start']) -_OUTPUT.fields_by_name['time_start'].containing_oneof = _OUTPUT.oneofs_by_name['_time_start'] -_OUTPUT.oneofs_by_name['_time_end'].fields.append( - _OUTPUT.fields_by_name['time_end']) -_OUTPUT.fields_by_name['time_end'].containing_oneof = _OUTPUT.oneofs_by_name['_time_end'] -DESCRIPTOR.message_types_by_name['EmptyMitigator'] = _EMPTYMITIGATOR -DESCRIPTOR.message_types_by_name['Output'] = _OUTPUT +DESCRIPTOR.message_types_by_name['L3AttackmitigatorOutput'] = _L3ATTACKMITIGATOROUTPUT _sym_db.RegisterFileDescriptor(DESCRIPTOR) -EmptyMitigator = _reflection.GeneratedProtocolMessageType('EmptyMitigator', (_message.Message,), { - 'DESCRIPTOR' : _EMPTYMITIGATOR, - '__module__' : 'l3_attackmitigator_pb2' - # @@protoc_insertion_point(class_scope:EmptyMitigator) - }) -_sym_db.RegisterMessage(EmptyMitigator) - -Output = _reflection.GeneratedProtocolMessageType('Output', (_message.Message,), { - 'DESCRIPTOR' : _OUTPUT, +L3AttackmitigatorOutput = _reflection.GeneratedProtocolMessageType('L3AttackmitigatorOutput', (_message.Message,), { + 'DESCRIPTOR' : _L3ATTACKMITIGATOROUTPUT, '__module__' : 'l3_attackmitigator_pb2' - # @@protoc_insertion_point(class_scope:Output) + # @@protoc_insertion_point(class_scope:L3AttackmitigatorOutput) }) -_sym_db.RegisterMessage(Output) +_sym_db.RegisterMessage(L3AttackmitigatorOutput) @@ -217,16 +147,16 @@ _L3ATTACKMITIGATOR = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=332, - serialized_end=446, + serialized_start=260, + serialized_end=388, methods=[ _descriptor.MethodDescriptor( name='SendOutput', full_name='L3Attackmitigator.SendOutput', index=0, containing_service=None, - input_type=_OUTPUT, - output_type=_EMPTYMITIGATOR, + input_type=_L3ATTACKMITIGATOROUTPUT, + output_type=context__pb2._EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, ), @@ -235,8 +165,8 @@ _L3ATTACKMITIGATOR = _descriptor.ServiceDescriptor( full_name='L3Attackmitigator.GetMitigation', index=1, containing_service=None, - input_type=_EMPTYMITIGATOR, - output_type=_EMPTYMITIGATOR, + input_type=context__pb2._EMPTY, + output_type=context__pb2._EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, ), diff --git a/src/l3_centralizedattackdetector/proto/l3_attackmitigator_pb2_grpc.py b/src/l3_centralizedattackdetector/proto/l3_attackmitigator_pb2_grpc.py index 3942d6843..25d4afdba 100644 --- a/src/l3_centralizedattackdetector/proto/l3_attackmitigator_pb2_grpc.py +++ b/src/l3_centralizedattackdetector/proto/l3_attackmitigator_pb2_grpc.py @@ -2,7 +2,7 @@ """Client and server classes corresponding to protobuf-defined services.""" import grpc - +from . import context_pb2 as context__pb2 from . import l3_attackmitigator_pb2 as l3__attackmitigator__pb2 @@ -17,13 +17,13 @@ class L3AttackmitigatorStub(object): """ self.SendOutput = channel.unary_unary( '/L3Attackmitigator/SendOutput', - request_serializer=l3__attackmitigator__pb2.Output.SerializeToString, - response_deserializer=l3__attackmitigator__pb2.EmptyMitigator.FromString, + request_serializer=l3__attackmitigator__pb2.L3AttackmitigatorOutput.SerializeToString, + response_deserializer=context__pb2.Empty.FromString, ) self.GetMitigation = channel.unary_unary( '/L3Attackmitigator/GetMitigation', - request_serializer=l3__attackmitigator__pb2.EmptyMitigator.SerializeToString, - response_deserializer=l3__attackmitigator__pb2.EmptyMitigator.FromString, + request_serializer=context__pb2.Empty.SerializeToString, + response_deserializer=context__pb2.Empty.FromString, ) @@ -49,13 +49,13 @@ def add_L3AttackmitigatorServicer_to_server(servicer, server): rpc_method_handlers = { 'SendOutput': grpc.unary_unary_rpc_method_handler( servicer.SendOutput, - request_deserializer=l3__attackmitigator__pb2.Output.FromString, - response_serializer=l3__attackmitigator__pb2.EmptyMitigator.SerializeToString, + request_deserializer=l3__attackmitigator__pb2.L3AttackmitigatorOutput.FromString, + response_serializer=context__pb2.Empty.SerializeToString, ), 'GetMitigation': grpc.unary_unary_rpc_method_handler( servicer.GetMitigation, - request_deserializer=l3__attackmitigator__pb2.EmptyMitigator.FromString, - response_serializer=l3__attackmitigator__pb2.EmptyMitigator.SerializeToString, + request_deserializer=context__pb2.Empty.FromString, + response_serializer=context__pb2.Empty.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( @@ -79,8 +79,8 @@ class L3Attackmitigator(object): timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/L3Attackmitigator/SendOutput', - l3__attackmitigator__pb2.Output.SerializeToString, - l3__attackmitigator__pb2.EmptyMitigator.FromString, + l3__attackmitigator__pb2.L3AttackmitigatorOutput.SerializeToString, + context__pb2.Empty.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @@ -96,7 +96,7 @@ class L3Attackmitigator(object): timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/L3Attackmitigator/GetMitigation', - l3__attackmitigator__pb2.EmptyMitigator.SerializeToString, - l3__attackmitigator__pb2.EmptyMitigator.FromString, + context__pb2.Empty.SerializeToString, + context__pb2.Empty.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/l3_centralizedattackdetector/proto/l3_centralizedattackdetector_pb2.py b/src/l3_centralizedattackdetector/proto/l3_centralizedattackdetector_pb2.py index cafbc6bf8..517fdb84b 100644 --- a/src/l3_centralizedattackdetector/proto/l3_centralizedattackdetector_pb2.py +++ b/src/l3_centralizedattackdetector/proto/l3_centralizedattackdetector_pb2.py @@ -19,127 +19,127 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\"l3_centralizedattackdetector.proto\"\xd9\x03\n\nModelInput\x12 \n\x18n_packets_server_seconds\x18\x01 \x01(\x02\x12 \n\x18n_packets_client_seconds\x18\x02 \x01(\x02\x12\x1d\n\x15n_bits_server_seconds\x18\x03 \x01(\x02\x12\x1d\n\x15n_bits_client_seconds\x18\x04 \x01(\x02\x12&\n\x1en_bits_server_n_packets_server\x18\x05 \x01(\x02\x12&\n\x1en_bits_client_n_packets_client\x18\x06 \x01(\x02\x12)\n!n_packets_server_n_packets_client\x18\x07 \x01(\x02\x12#\n\x1bn_bits_server_n_bits_client\x18\x08 \x01(\x02\x12\x0c\n\x04ip_o\x18\t \x01(\t\x12\x0e\n\x06port_o\x18\n \x01(\t\x12\x0c\n\x04ip_d\x18\x0b \x01(\t\x12\x0e\n\x06port_d\x18\x0c \x01(\t\x12\x0f\n\x07\x66low_id\x18\r \x01(\t\x12\x10\n\x08protocol\x18\x0e \x01(\t\x12\x17\n\ntime_start\x18\x0f \x01(\x02H\x00\x88\x01\x01\x12\x15\n\x08time_end\x18\x10 \x01(\x02H\x01\x88\x01\x01\x42\r\n\x0b_time_startB\x0b\n\t_time_end\")\n\x05\x45mpty\x12\x14\n\x07message\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_message\"\xfe\x01\n\x0bModelOutput\x12\x12\n\nconfidence\x18\x01 \x01(\x02\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\x0c\n\x04ip_o\x18\x03 \x01(\t\x12\x10\n\x08tag_name\x18\x04 \x01(\t\x12\x0b\n\x03tag\x18\x05 \x01(\x05\x12\x0f\n\x07\x66low_id\x18\x06 \x01(\t\x12\x10\n\x08protocol\x18\x07 \x01(\t\x12\x0e\n\x06port_d\x18\x08 \x01(\t\x12\x12\n\x05ml_id\x18\t \x01(\tH\x00\x88\x01\x01\x12\x17\n\ntime_start\x18\n \x01(\x02H\x01\x88\x01\x01\x12\x15\n\x08time_end\x18\x0b \x01(\x02H\x02\x88\x01\x01\x42\x08\n\x06_ml_idB\r\n\x0b_time_startB\x0b\n\t_time_end2f\n\x1bL3Centralizedattackdetector\x12\"\n\tSendInput\x12\x0b.ModelInput\x1a\x06.Empty\"\x00\x12#\n\tGetOutput\x12\x06.Empty\x1a\x0c.ModelOutput\"\x00\x62\x06proto3' + serialized_pb=b'\n\"l3_centralizedattackdetector.proto\"\xcb\x03\n\"L3CentralizedattackdetectorMetrics\x12 \n\x18n_packets_server_seconds\x18\x01 \x01(\x02\x12 \n\x18n_packets_client_seconds\x18\x02 \x01(\x02\x12\x1d\n\x15n_bits_server_seconds\x18\x03 \x01(\x02\x12\x1d\n\x15n_bits_client_seconds\x18\x04 \x01(\x02\x12&\n\x1en_bits_server_n_packets_server\x18\x05 \x01(\x02\x12&\n\x1en_bits_client_n_packets_client\x18\x06 \x01(\x02\x12)\n!n_packets_server_n_packets_client\x18\x07 \x01(\x02\x12#\n\x1bn_bits_server_n_bits_client\x18\x08 \x01(\x02\x12\x0c\n\x04ip_o\x18\t \x01(\t\x12\x0e\n\x06port_o\x18\n \x01(\t\x12\x0c\n\x04ip_d\x18\x0b \x01(\t\x12\x0e\n\x06port_d\x18\x0c \x01(\t\x12\x0f\n\x07\x66low_id\x18\r \x01(\t\x12\x10\n\x08protocol\x18\x0e \x01(\t\x12\x12\n\ntime_start\x18\x0f \x01(\x02\x12\x10\n\x08time_end\x18\x10 \x01(\x02\"\x18\n\x05\x45mpty\x12\x0f\n\x07message\x18\x01 \x01(\t\"\xe4\x01\n&L3CentralizedattackdetectorModelOutput\x12\x12\n\nconfidence\x18\x01 \x01(\x02\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\x0c\n\x04ip_o\x18\x03 \x01(\t\x12\x10\n\x08tag_name\x18\x04 \x01(\t\x12\x0b\n\x03tag\x18\x05 \x01(\x05\x12\x0f\n\x07\x66low_id\x18\x06 \x01(\t\x12\x10\n\x08protocol\x18\x07 \x01(\t\x12\x0e\n\x06port_d\x18\x08 \x01(\t\x12\r\n\x05ml_id\x18\t \x01(\t\x12\x12\n\ntime_start\x18\n \x01(\x02\x12\x10\n\x08time_end\x18\x0b \x01(\x02\x32\x99\x01\n\x1bL3Centralizedattackdetector\x12:\n\tSendInput\x12#.L3CentralizedattackdetectorMetrics\x1a\x06.Empty\"\x00\x12>\n\tGetOutput\x12\x06.Empty\x1a\'.L3CentralizedattackdetectorModelOutput\"\x00\x62\x06proto3' ) -_MODELINPUT = _descriptor.Descriptor( - name='ModelInput', - full_name='ModelInput', +_L3CENTRALIZEDATTACKDETECTORMETRICS = _descriptor.Descriptor( + name='L3CentralizedattackdetectorMetrics', + full_name='L3CentralizedattackdetectorMetrics', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='n_packets_server_seconds', full_name='ModelInput.n_packets_server_seconds', index=0, + name='n_packets_server_seconds', full_name='L3CentralizedattackdetectorMetrics.n_packets_server_seconds', index=0, number=1, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_packets_client_seconds', full_name='ModelInput.n_packets_client_seconds', index=1, + name='n_packets_client_seconds', full_name='L3CentralizedattackdetectorMetrics.n_packets_client_seconds', index=1, number=2, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_bits_server_seconds', full_name='ModelInput.n_bits_server_seconds', index=2, + name='n_bits_server_seconds', full_name='L3CentralizedattackdetectorMetrics.n_bits_server_seconds', index=2, number=3, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_bits_client_seconds', full_name='ModelInput.n_bits_client_seconds', index=3, + name='n_bits_client_seconds', full_name='L3CentralizedattackdetectorMetrics.n_bits_client_seconds', index=3, number=4, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_bits_server_n_packets_server', full_name='ModelInput.n_bits_server_n_packets_server', index=4, + name='n_bits_server_n_packets_server', full_name='L3CentralizedattackdetectorMetrics.n_bits_server_n_packets_server', index=4, number=5, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_bits_client_n_packets_client', full_name='ModelInput.n_bits_client_n_packets_client', index=5, + name='n_bits_client_n_packets_client', full_name='L3CentralizedattackdetectorMetrics.n_bits_client_n_packets_client', index=5, number=6, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_packets_server_n_packets_client', full_name='ModelInput.n_packets_server_n_packets_client', index=6, + name='n_packets_server_n_packets_client', full_name='L3CentralizedattackdetectorMetrics.n_packets_server_n_packets_client', index=6, number=7, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_bits_server_n_bits_client', full_name='ModelInput.n_bits_server_n_bits_client', index=7, + name='n_bits_server_n_bits_client', full_name='L3CentralizedattackdetectorMetrics.n_bits_server_n_bits_client', index=7, number=8, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='ip_o', full_name='ModelInput.ip_o', index=8, + name='ip_o', full_name='L3CentralizedattackdetectorMetrics.ip_o', index=8, number=9, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='port_o', full_name='ModelInput.port_o', index=9, + name='port_o', full_name='L3CentralizedattackdetectorMetrics.port_o', index=9, number=10, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='ip_d', full_name='ModelInput.ip_d', index=10, + name='ip_d', full_name='L3CentralizedattackdetectorMetrics.ip_d', index=10, number=11, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='port_d', full_name='ModelInput.port_d', index=11, + name='port_d', full_name='L3CentralizedattackdetectorMetrics.port_d', index=11, number=12, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='flow_id', full_name='ModelInput.flow_id', index=12, + name='flow_id', full_name='L3CentralizedattackdetectorMetrics.flow_id', index=12, number=13, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='protocol', full_name='ModelInput.protocol', index=13, + name='protocol', full_name='L3CentralizedattackdetectorMetrics.protocol', index=13, number=14, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='time_start', full_name='ModelInput.time_start', index=14, + name='time_start', full_name='L3CentralizedattackdetectorMetrics.time_start', index=14, number=15, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='time_end', full_name='ModelInput.time_end', index=15, + name='time_end', full_name='L3CentralizedattackdetectorMetrics.time_end', index=15, number=16, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(0), message_type=None, enum_type=None, containing_type=None, @@ -156,19 +156,9 @@ _MODELINPUT = _descriptor.Descriptor( syntax='proto3', extension_ranges=[], oneofs=[ - _descriptor.OneofDescriptor( - name='_time_start', full_name='ModelInput._time_start', - index=0, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), - _descriptor.OneofDescriptor( - name='_time_end', full_name='ModelInput._time_end', - index=1, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), ], serialized_start=39, - serialized_end=512, + serialized_end=498, ) @@ -198,97 +188,92 @@ _EMPTY = _descriptor.Descriptor( syntax='proto3', extension_ranges=[], oneofs=[ - _descriptor.OneofDescriptor( - name='_message', full_name='Empty._message', - index=0, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), ], - serialized_start=514, - serialized_end=555, + serialized_start=500, + serialized_end=524, ) -_MODELOUTPUT = _descriptor.Descriptor( - name='ModelOutput', - full_name='ModelOutput', +_L3CENTRALIZEDATTACKDETECTORMODELOUTPUT = _descriptor.Descriptor( + name='L3CentralizedattackdetectorModelOutput', + full_name='L3CentralizedattackdetectorModelOutput', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='confidence', full_name='ModelOutput.confidence', index=0, + name='confidence', full_name='L3CentralizedattackdetectorModelOutput.confidence', index=0, number=1, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='timestamp', full_name='ModelOutput.timestamp', index=1, + name='timestamp', full_name='L3CentralizedattackdetectorModelOutput.timestamp', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='ip_o', full_name='ModelOutput.ip_o', index=2, + name='ip_o', full_name='L3CentralizedattackdetectorModelOutput.ip_o', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='tag_name', full_name='ModelOutput.tag_name', index=3, + name='tag_name', full_name='L3CentralizedattackdetectorModelOutput.tag_name', index=3, number=4, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='tag', full_name='ModelOutput.tag', index=4, + name='tag', full_name='L3CentralizedattackdetectorModelOutput.tag', index=4, number=5, type=5, cpp_type=1, 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), _descriptor.FieldDescriptor( - name='flow_id', full_name='ModelOutput.flow_id', index=5, + name='flow_id', full_name='L3CentralizedattackdetectorModelOutput.flow_id', index=5, number=6, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='protocol', full_name='ModelOutput.protocol', index=6, + name='protocol', full_name='L3CentralizedattackdetectorModelOutput.protocol', index=6, number=7, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='port_d', full_name='ModelOutput.port_d', index=7, + name='port_d', full_name='L3CentralizedattackdetectorModelOutput.port_d', index=7, number=8, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='ml_id', full_name='ModelOutput.ml_id', index=8, + name='ml_id', full_name='L3CentralizedattackdetectorModelOutput.ml_id', index=8, number=9, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='time_start', full_name='ModelOutput.time_start', index=9, + name='time_start', full_name='L3CentralizedattackdetectorModelOutput.time_start', index=9, number=10, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='time_end', full_name='ModelOutput.time_end', index=10, + name='time_end', full_name='L3CentralizedattackdetectorModelOutput.time_end', index=10, number=11, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(0), message_type=None, enum_type=None, containing_type=None, @@ -305,55 +290,22 @@ _MODELOUTPUT = _descriptor.Descriptor( syntax='proto3', extension_ranges=[], oneofs=[ - _descriptor.OneofDescriptor( - name='_ml_id', full_name='ModelOutput._ml_id', - index=0, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), - _descriptor.OneofDescriptor( - name='_time_start', full_name='ModelOutput._time_start', - index=1, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), - _descriptor.OneofDescriptor( - name='_time_end', full_name='ModelOutput._time_end', - index=2, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), ], - serialized_start=558, - serialized_end=812, + serialized_start=527, + serialized_end=755, ) -_MODELINPUT.oneofs_by_name['_time_start'].fields.append( - _MODELINPUT.fields_by_name['time_start']) -_MODELINPUT.fields_by_name['time_start'].containing_oneof = _MODELINPUT.oneofs_by_name['_time_start'] -_MODELINPUT.oneofs_by_name['_time_end'].fields.append( - _MODELINPUT.fields_by_name['time_end']) -_MODELINPUT.fields_by_name['time_end'].containing_oneof = _MODELINPUT.oneofs_by_name['_time_end'] -_EMPTY.oneofs_by_name['_message'].fields.append( - _EMPTY.fields_by_name['message']) -_EMPTY.fields_by_name['message'].containing_oneof = _EMPTY.oneofs_by_name['_message'] -_MODELOUTPUT.oneofs_by_name['_ml_id'].fields.append( - _MODELOUTPUT.fields_by_name['ml_id']) -_MODELOUTPUT.fields_by_name['ml_id'].containing_oneof = _MODELOUTPUT.oneofs_by_name['_ml_id'] -_MODELOUTPUT.oneofs_by_name['_time_start'].fields.append( - _MODELOUTPUT.fields_by_name['time_start']) -_MODELOUTPUT.fields_by_name['time_start'].containing_oneof = _MODELOUTPUT.oneofs_by_name['_time_start'] -_MODELOUTPUT.oneofs_by_name['_time_end'].fields.append( - _MODELOUTPUT.fields_by_name['time_end']) -_MODELOUTPUT.fields_by_name['time_end'].containing_oneof = _MODELOUTPUT.oneofs_by_name['_time_end'] -DESCRIPTOR.message_types_by_name['ModelInput'] = _MODELINPUT +DESCRIPTOR.message_types_by_name['L3CentralizedattackdetectorMetrics'] = _L3CENTRALIZEDATTACKDETECTORMETRICS DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY -DESCRIPTOR.message_types_by_name['ModelOutput'] = _MODELOUTPUT +DESCRIPTOR.message_types_by_name['L3CentralizedattackdetectorModelOutput'] = _L3CENTRALIZEDATTACKDETECTORMODELOUTPUT _sym_db.RegisterFileDescriptor(DESCRIPTOR) -ModelInput = _reflection.GeneratedProtocolMessageType('ModelInput', (_message.Message,), { - 'DESCRIPTOR' : _MODELINPUT, +L3CentralizedattackdetectorMetrics = _reflection.GeneratedProtocolMessageType('L3CentralizedattackdetectorMetrics', (_message.Message,), { + 'DESCRIPTOR' : _L3CENTRALIZEDATTACKDETECTORMETRICS, '__module__' : 'l3_centralizedattackdetector_pb2' - # @@protoc_insertion_point(class_scope:ModelInput) + # @@protoc_insertion_point(class_scope:L3CentralizedattackdetectorMetrics) }) -_sym_db.RegisterMessage(ModelInput) +_sym_db.RegisterMessage(L3CentralizedattackdetectorMetrics) Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), { 'DESCRIPTOR' : _EMPTY, @@ -362,12 +314,12 @@ Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), { }) _sym_db.RegisterMessage(Empty) -ModelOutput = _reflection.GeneratedProtocolMessageType('ModelOutput', (_message.Message,), { - 'DESCRIPTOR' : _MODELOUTPUT, +L3CentralizedattackdetectorModelOutput = _reflection.GeneratedProtocolMessageType('L3CentralizedattackdetectorModelOutput', (_message.Message,), { + 'DESCRIPTOR' : _L3CENTRALIZEDATTACKDETECTORMODELOUTPUT, '__module__' : 'l3_centralizedattackdetector_pb2' - # @@protoc_insertion_point(class_scope:ModelOutput) + # @@protoc_insertion_point(class_scope:L3CentralizedattackdetectorModelOutput) }) -_sym_db.RegisterMessage(ModelOutput) +_sym_db.RegisterMessage(L3CentralizedattackdetectorModelOutput) @@ -378,15 +330,15 @@ _L3CENTRALIZEDATTACKDETECTOR = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=814, - serialized_end=916, + serialized_start=758, + serialized_end=911, methods=[ _descriptor.MethodDescriptor( name='SendInput', full_name='L3Centralizedattackdetector.SendInput', index=0, containing_service=None, - input_type=_MODELINPUT, + input_type=_L3CENTRALIZEDATTACKDETECTORMETRICS, output_type=_EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, @@ -397,7 +349,7 @@ _L3CENTRALIZEDATTACKDETECTOR = _descriptor.ServiceDescriptor( index=1, containing_service=None, input_type=_EMPTY, - output_type=_MODELOUTPUT, + output_type=_L3CENTRALIZEDATTACKDETECTORMODELOUTPUT, serialized_options=None, create_key=_descriptor._internal_create_key, ), diff --git a/src/l3_centralizedattackdetector/proto/l3_centralizedattackdetector_pb2_grpc.py b/src/l3_centralizedattackdetector/proto/l3_centralizedattackdetector_pb2_grpc.py index eaff68364..29136b4da 100644 --- a/src/l3_centralizedattackdetector/proto/l3_centralizedattackdetector_pb2_grpc.py +++ b/src/l3_centralizedattackdetector/proto/l3_centralizedattackdetector_pb2_grpc.py @@ -16,13 +16,13 @@ class L3CentralizedattackdetectorStub(object): """ self.SendInput = channel.unary_unary( '/L3Centralizedattackdetector/SendInput', - request_serializer=l3__centralizedattackdetector__pb2.ModelInput.SerializeToString, + request_serializer=l3__centralizedattackdetector__pb2.L3CentralizedattackdetectorMetrics.SerializeToString, response_deserializer=l3__centralizedattackdetector__pb2.Empty.FromString, ) self.GetOutput = channel.unary_unary( '/L3Centralizedattackdetector/GetOutput', request_serializer=l3__centralizedattackdetector__pb2.Empty.SerializeToString, - response_deserializer=l3__centralizedattackdetector__pb2.ModelOutput.FromString, + response_deserializer=l3__centralizedattackdetector__pb2.L3CentralizedattackdetectorModelOutput.FromString, ) @@ -48,13 +48,13 @@ def add_L3CentralizedattackdetectorServicer_to_server(servicer, server): rpc_method_handlers = { 'SendInput': grpc.unary_unary_rpc_method_handler( servicer.SendInput, - request_deserializer=l3__centralizedattackdetector__pb2.ModelInput.FromString, + request_deserializer=l3__centralizedattackdetector__pb2.L3CentralizedattackdetectorMetrics.FromString, response_serializer=l3__centralizedattackdetector__pb2.Empty.SerializeToString, ), 'GetOutput': grpc.unary_unary_rpc_method_handler( servicer.GetOutput, request_deserializer=l3__centralizedattackdetector__pb2.Empty.FromString, - response_serializer=l3__centralizedattackdetector__pb2.ModelOutput.SerializeToString, + response_serializer=l3__centralizedattackdetector__pb2.L3CentralizedattackdetectorModelOutput.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( @@ -78,7 +78,7 @@ class L3Centralizedattackdetector(object): timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/L3Centralizedattackdetector/SendInput', - l3__centralizedattackdetector__pb2.ModelInput.SerializeToString, + l3__centralizedattackdetector__pb2.L3CentralizedattackdetectorMetrics.SerializeToString, l3__centralizedattackdetector__pb2.Empty.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @@ -96,6 +96,6 @@ class L3Centralizedattackdetector(object): metadata=None): return grpc.experimental.unary_unary(request, target, '/L3Centralizedattackdetector/GetOutput', l3__centralizedattackdetector__pb2.Empty.SerializeToString, - l3__centralizedattackdetector__pb2.ModelOutput.FromString, + l3__centralizedattackdetector__pb2.L3CentralizedattackdetectorModelOutput.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/l3_distributedattackdetector/genproto.sh b/src/l3_distributedattackdetector/genproto.sh old mode 100644 new mode 100755 diff --git a/src/l3_distributedattackdetector/proto/__init__.py b/src/l3_distributedattackdetector/proto/__init__.py index 70a332512..e69de29bb 100644 --- a/src/l3_distributedattackdetector/proto/__init__.py +++ b/src/l3_distributedattackdetector/proto/__init__.py @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/l3_distributedattackdetector/proto/l3_centralizedattackdetector_pb2.py b/src/l3_distributedattackdetector/proto/l3_centralizedattackdetector_pb2.py index cafbc6bf8..517fdb84b 100644 --- a/src/l3_distributedattackdetector/proto/l3_centralizedattackdetector_pb2.py +++ b/src/l3_distributedattackdetector/proto/l3_centralizedattackdetector_pb2.py @@ -19,127 +19,127 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\"l3_centralizedattackdetector.proto\"\xd9\x03\n\nModelInput\x12 \n\x18n_packets_server_seconds\x18\x01 \x01(\x02\x12 \n\x18n_packets_client_seconds\x18\x02 \x01(\x02\x12\x1d\n\x15n_bits_server_seconds\x18\x03 \x01(\x02\x12\x1d\n\x15n_bits_client_seconds\x18\x04 \x01(\x02\x12&\n\x1en_bits_server_n_packets_server\x18\x05 \x01(\x02\x12&\n\x1en_bits_client_n_packets_client\x18\x06 \x01(\x02\x12)\n!n_packets_server_n_packets_client\x18\x07 \x01(\x02\x12#\n\x1bn_bits_server_n_bits_client\x18\x08 \x01(\x02\x12\x0c\n\x04ip_o\x18\t \x01(\t\x12\x0e\n\x06port_o\x18\n \x01(\t\x12\x0c\n\x04ip_d\x18\x0b \x01(\t\x12\x0e\n\x06port_d\x18\x0c \x01(\t\x12\x0f\n\x07\x66low_id\x18\r \x01(\t\x12\x10\n\x08protocol\x18\x0e \x01(\t\x12\x17\n\ntime_start\x18\x0f \x01(\x02H\x00\x88\x01\x01\x12\x15\n\x08time_end\x18\x10 \x01(\x02H\x01\x88\x01\x01\x42\r\n\x0b_time_startB\x0b\n\t_time_end\")\n\x05\x45mpty\x12\x14\n\x07message\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\n\n\x08_message\"\xfe\x01\n\x0bModelOutput\x12\x12\n\nconfidence\x18\x01 \x01(\x02\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\x0c\n\x04ip_o\x18\x03 \x01(\t\x12\x10\n\x08tag_name\x18\x04 \x01(\t\x12\x0b\n\x03tag\x18\x05 \x01(\x05\x12\x0f\n\x07\x66low_id\x18\x06 \x01(\t\x12\x10\n\x08protocol\x18\x07 \x01(\t\x12\x0e\n\x06port_d\x18\x08 \x01(\t\x12\x12\n\x05ml_id\x18\t \x01(\tH\x00\x88\x01\x01\x12\x17\n\ntime_start\x18\n \x01(\x02H\x01\x88\x01\x01\x12\x15\n\x08time_end\x18\x0b \x01(\x02H\x02\x88\x01\x01\x42\x08\n\x06_ml_idB\r\n\x0b_time_startB\x0b\n\t_time_end2f\n\x1bL3Centralizedattackdetector\x12\"\n\tSendInput\x12\x0b.ModelInput\x1a\x06.Empty\"\x00\x12#\n\tGetOutput\x12\x06.Empty\x1a\x0c.ModelOutput\"\x00\x62\x06proto3' + serialized_pb=b'\n\"l3_centralizedattackdetector.proto\"\xcb\x03\n\"L3CentralizedattackdetectorMetrics\x12 \n\x18n_packets_server_seconds\x18\x01 \x01(\x02\x12 \n\x18n_packets_client_seconds\x18\x02 \x01(\x02\x12\x1d\n\x15n_bits_server_seconds\x18\x03 \x01(\x02\x12\x1d\n\x15n_bits_client_seconds\x18\x04 \x01(\x02\x12&\n\x1en_bits_server_n_packets_server\x18\x05 \x01(\x02\x12&\n\x1en_bits_client_n_packets_client\x18\x06 \x01(\x02\x12)\n!n_packets_server_n_packets_client\x18\x07 \x01(\x02\x12#\n\x1bn_bits_server_n_bits_client\x18\x08 \x01(\x02\x12\x0c\n\x04ip_o\x18\t \x01(\t\x12\x0e\n\x06port_o\x18\n \x01(\t\x12\x0c\n\x04ip_d\x18\x0b \x01(\t\x12\x0e\n\x06port_d\x18\x0c \x01(\t\x12\x0f\n\x07\x66low_id\x18\r \x01(\t\x12\x10\n\x08protocol\x18\x0e \x01(\t\x12\x12\n\ntime_start\x18\x0f \x01(\x02\x12\x10\n\x08time_end\x18\x10 \x01(\x02\"\x18\n\x05\x45mpty\x12\x0f\n\x07message\x18\x01 \x01(\t\"\xe4\x01\n&L3CentralizedattackdetectorModelOutput\x12\x12\n\nconfidence\x18\x01 \x01(\x02\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\x0c\n\x04ip_o\x18\x03 \x01(\t\x12\x10\n\x08tag_name\x18\x04 \x01(\t\x12\x0b\n\x03tag\x18\x05 \x01(\x05\x12\x0f\n\x07\x66low_id\x18\x06 \x01(\t\x12\x10\n\x08protocol\x18\x07 \x01(\t\x12\x0e\n\x06port_d\x18\x08 \x01(\t\x12\r\n\x05ml_id\x18\t \x01(\t\x12\x12\n\ntime_start\x18\n \x01(\x02\x12\x10\n\x08time_end\x18\x0b \x01(\x02\x32\x99\x01\n\x1bL3Centralizedattackdetector\x12:\n\tSendInput\x12#.L3CentralizedattackdetectorMetrics\x1a\x06.Empty\"\x00\x12>\n\tGetOutput\x12\x06.Empty\x1a\'.L3CentralizedattackdetectorModelOutput\"\x00\x62\x06proto3' ) -_MODELINPUT = _descriptor.Descriptor( - name='ModelInput', - full_name='ModelInput', +_L3CENTRALIZEDATTACKDETECTORMETRICS = _descriptor.Descriptor( + name='L3CentralizedattackdetectorMetrics', + full_name='L3CentralizedattackdetectorMetrics', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='n_packets_server_seconds', full_name='ModelInput.n_packets_server_seconds', index=0, + name='n_packets_server_seconds', full_name='L3CentralizedattackdetectorMetrics.n_packets_server_seconds', index=0, number=1, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_packets_client_seconds', full_name='ModelInput.n_packets_client_seconds', index=1, + name='n_packets_client_seconds', full_name='L3CentralizedattackdetectorMetrics.n_packets_client_seconds', index=1, number=2, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_bits_server_seconds', full_name='ModelInput.n_bits_server_seconds', index=2, + name='n_bits_server_seconds', full_name='L3CentralizedattackdetectorMetrics.n_bits_server_seconds', index=2, number=3, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_bits_client_seconds', full_name='ModelInput.n_bits_client_seconds', index=3, + name='n_bits_client_seconds', full_name='L3CentralizedattackdetectorMetrics.n_bits_client_seconds', index=3, number=4, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_bits_server_n_packets_server', full_name='ModelInput.n_bits_server_n_packets_server', index=4, + name='n_bits_server_n_packets_server', full_name='L3CentralizedattackdetectorMetrics.n_bits_server_n_packets_server', index=4, number=5, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_bits_client_n_packets_client', full_name='ModelInput.n_bits_client_n_packets_client', index=5, + name='n_bits_client_n_packets_client', full_name='L3CentralizedattackdetectorMetrics.n_bits_client_n_packets_client', index=5, number=6, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_packets_server_n_packets_client', full_name='ModelInput.n_packets_server_n_packets_client', index=6, + name='n_packets_server_n_packets_client', full_name='L3CentralizedattackdetectorMetrics.n_packets_server_n_packets_client', index=6, number=7, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='n_bits_server_n_bits_client', full_name='ModelInput.n_bits_server_n_bits_client', index=7, + name='n_bits_server_n_bits_client', full_name='L3CentralizedattackdetectorMetrics.n_bits_server_n_bits_client', index=7, number=8, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='ip_o', full_name='ModelInput.ip_o', index=8, + name='ip_o', full_name='L3CentralizedattackdetectorMetrics.ip_o', index=8, number=9, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='port_o', full_name='ModelInput.port_o', index=9, + name='port_o', full_name='L3CentralizedattackdetectorMetrics.port_o', index=9, number=10, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='ip_d', full_name='ModelInput.ip_d', index=10, + name='ip_d', full_name='L3CentralizedattackdetectorMetrics.ip_d', index=10, number=11, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='port_d', full_name='ModelInput.port_d', index=11, + name='port_d', full_name='L3CentralizedattackdetectorMetrics.port_d', index=11, number=12, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='flow_id', full_name='ModelInput.flow_id', index=12, + name='flow_id', full_name='L3CentralizedattackdetectorMetrics.flow_id', index=12, number=13, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='protocol', full_name='ModelInput.protocol', index=13, + name='protocol', full_name='L3CentralizedattackdetectorMetrics.protocol', index=13, number=14, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='time_start', full_name='ModelInput.time_start', index=14, + name='time_start', full_name='L3CentralizedattackdetectorMetrics.time_start', index=14, number=15, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='time_end', full_name='ModelInput.time_end', index=15, + name='time_end', full_name='L3CentralizedattackdetectorMetrics.time_end', index=15, number=16, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(0), message_type=None, enum_type=None, containing_type=None, @@ -156,19 +156,9 @@ _MODELINPUT = _descriptor.Descriptor( syntax='proto3', extension_ranges=[], oneofs=[ - _descriptor.OneofDescriptor( - name='_time_start', full_name='ModelInput._time_start', - index=0, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), - _descriptor.OneofDescriptor( - name='_time_end', full_name='ModelInput._time_end', - index=1, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), ], serialized_start=39, - serialized_end=512, + serialized_end=498, ) @@ -198,97 +188,92 @@ _EMPTY = _descriptor.Descriptor( syntax='proto3', extension_ranges=[], oneofs=[ - _descriptor.OneofDescriptor( - name='_message', full_name='Empty._message', - index=0, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), ], - serialized_start=514, - serialized_end=555, + serialized_start=500, + serialized_end=524, ) -_MODELOUTPUT = _descriptor.Descriptor( - name='ModelOutput', - full_name='ModelOutput', +_L3CENTRALIZEDATTACKDETECTORMODELOUTPUT = _descriptor.Descriptor( + name='L3CentralizedattackdetectorModelOutput', + full_name='L3CentralizedattackdetectorModelOutput', filename=None, file=DESCRIPTOR, containing_type=None, create_key=_descriptor._internal_create_key, fields=[ _descriptor.FieldDescriptor( - name='confidence', full_name='ModelOutput.confidence', index=0, + name='confidence', full_name='L3CentralizedattackdetectorModelOutput.confidence', index=0, number=1, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='timestamp', full_name='ModelOutput.timestamp', index=1, + name='timestamp', full_name='L3CentralizedattackdetectorModelOutput.timestamp', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='ip_o', full_name='ModelOutput.ip_o', index=2, + name='ip_o', full_name='L3CentralizedattackdetectorModelOutput.ip_o', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='tag_name', full_name='ModelOutput.tag_name', index=3, + name='tag_name', full_name='L3CentralizedattackdetectorModelOutput.tag_name', index=3, number=4, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='tag', full_name='ModelOutput.tag', index=4, + name='tag', full_name='L3CentralizedattackdetectorModelOutput.tag', index=4, number=5, type=5, cpp_type=1, 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), _descriptor.FieldDescriptor( - name='flow_id', full_name='ModelOutput.flow_id', index=5, + name='flow_id', full_name='L3CentralizedattackdetectorModelOutput.flow_id', index=5, number=6, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='protocol', full_name='ModelOutput.protocol', index=6, + name='protocol', full_name='L3CentralizedattackdetectorModelOutput.protocol', index=6, number=7, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='port_d', full_name='ModelOutput.port_d', index=7, + name='port_d', full_name='L3CentralizedattackdetectorModelOutput.port_d', index=7, number=8, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='ml_id', full_name='ModelOutput.ml_id', index=8, + name='ml_id', full_name='L3CentralizedattackdetectorModelOutput.ml_id', index=8, number=9, type=9, cpp_type=9, label=1, has_default_value=False, default_value=b"".decode('utf-8'), 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='time_start', full_name='ModelOutput.time_start', index=9, + name='time_start', full_name='L3CentralizedattackdetectorModelOutput.time_start', index=9, number=10, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(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), _descriptor.FieldDescriptor( - name='time_end', full_name='ModelOutput.time_end', index=10, + name='time_end', full_name='L3CentralizedattackdetectorModelOutput.time_end', index=10, number=11, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(0), message_type=None, enum_type=None, containing_type=None, @@ -305,55 +290,22 @@ _MODELOUTPUT = _descriptor.Descriptor( syntax='proto3', extension_ranges=[], oneofs=[ - _descriptor.OneofDescriptor( - name='_ml_id', full_name='ModelOutput._ml_id', - index=0, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), - _descriptor.OneofDescriptor( - name='_time_start', full_name='ModelOutput._time_start', - index=1, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), - _descriptor.OneofDescriptor( - name='_time_end', full_name='ModelOutput._time_end', - index=2, containing_type=None, - create_key=_descriptor._internal_create_key, - fields=[]), ], - serialized_start=558, - serialized_end=812, + serialized_start=527, + serialized_end=755, ) -_MODELINPUT.oneofs_by_name['_time_start'].fields.append( - _MODELINPUT.fields_by_name['time_start']) -_MODELINPUT.fields_by_name['time_start'].containing_oneof = _MODELINPUT.oneofs_by_name['_time_start'] -_MODELINPUT.oneofs_by_name['_time_end'].fields.append( - _MODELINPUT.fields_by_name['time_end']) -_MODELINPUT.fields_by_name['time_end'].containing_oneof = _MODELINPUT.oneofs_by_name['_time_end'] -_EMPTY.oneofs_by_name['_message'].fields.append( - _EMPTY.fields_by_name['message']) -_EMPTY.fields_by_name['message'].containing_oneof = _EMPTY.oneofs_by_name['_message'] -_MODELOUTPUT.oneofs_by_name['_ml_id'].fields.append( - _MODELOUTPUT.fields_by_name['ml_id']) -_MODELOUTPUT.fields_by_name['ml_id'].containing_oneof = _MODELOUTPUT.oneofs_by_name['_ml_id'] -_MODELOUTPUT.oneofs_by_name['_time_start'].fields.append( - _MODELOUTPUT.fields_by_name['time_start']) -_MODELOUTPUT.fields_by_name['time_start'].containing_oneof = _MODELOUTPUT.oneofs_by_name['_time_start'] -_MODELOUTPUT.oneofs_by_name['_time_end'].fields.append( - _MODELOUTPUT.fields_by_name['time_end']) -_MODELOUTPUT.fields_by_name['time_end'].containing_oneof = _MODELOUTPUT.oneofs_by_name['_time_end'] -DESCRIPTOR.message_types_by_name['ModelInput'] = _MODELINPUT +DESCRIPTOR.message_types_by_name['L3CentralizedattackdetectorMetrics'] = _L3CENTRALIZEDATTACKDETECTORMETRICS DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY -DESCRIPTOR.message_types_by_name['ModelOutput'] = _MODELOUTPUT +DESCRIPTOR.message_types_by_name['L3CentralizedattackdetectorModelOutput'] = _L3CENTRALIZEDATTACKDETECTORMODELOUTPUT _sym_db.RegisterFileDescriptor(DESCRIPTOR) -ModelInput = _reflection.GeneratedProtocolMessageType('ModelInput', (_message.Message,), { - 'DESCRIPTOR' : _MODELINPUT, +L3CentralizedattackdetectorMetrics = _reflection.GeneratedProtocolMessageType('L3CentralizedattackdetectorMetrics', (_message.Message,), { + 'DESCRIPTOR' : _L3CENTRALIZEDATTACKDETECTORMETRICS, '__module__' : 'l3_centralizedattackdetector_pb2' - # @@protoc_insertion_point(class_scope:ModelInput) + # @@protoc_insertion_point(class_scope:L3CentralizedattackdetectorMetrics) }) -_sym_db.RegisterMessage(ModelInput) +_sym_db.RegisterMessage(L3CentralizedattackdetectorMetrics) Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), { 'DESCRIPTOR' : _EMPTY, @@ -362,12 +314,12 @@ Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), { }) _sym_db.RegisterMessage(Empty) -ModelOutput = _reflection.GeneratedProtocolMessageType('ModelOutput', (_message.Message,), { - 'DESCRIPTOR' : _MODELOUTPUT, +L3CentralizedattackdetectorModelOutput = _reflection.GeneratedProtocolMessageType('L3CentralizedattackdetectorModelOutput', (_message.Message,), { + 'DESCRIPTOR' : _L3CENTRALIZEDATTACKDETECTORMODELOUTPUT, '__module__' : 'l3_centralizedattackdetector_pb2' - # @@protoc_insertion_point(class_scope:ModelOutput) + # @@protoc_insertion_point(class_scope:L3CentralizedattackdetectorModelOutput) }) -_sym_db.RegisterMessage(ModelOutput) +_sym_db.RegisterMessage(L3CentralizedattackdetectorModelOutput) @@ -378,15 +330,15 @@ _L3CENTRALIZEDATTACKDETECTOR = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=814, - serialized_end=916, + serialized_start=758, + serialized_end=911, methods=[ _descriptor.MethodDescriptor( name='SendInput', full_name='L3Centralizedattackdetector.SendInput', index=0, containing_service=None, - input_type=_MODELINPUT, + input_type=_L3CENTRALIZEDATTACKDETECTORMETRICS, output_type=_EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, @@ -397,7 +349,7 @@ _L3CENTRALIZEDATTACKDETECTOR = _descriptor.ServiceDescriptor( index=1, containing_service=None, input_type=_EMPTY, - output_type=_MODELOUTPUT, + output_type=_L3CENTRALIZEDATTACKDETECTORMODELOUTPUT, serialized_options=None, create_key=_descriptor._internal_create_key, ), diff --git a/src/l3_distributedattackdetector/proto/l3_centralizedattackdetector_pb2_grpc.py b/src/l3_distributedattackdetector/proto/l3_centralizedattackdetector_pb2_grpc.py index eaff68364..29136b4da 100644 --- a/src/l3_distributedattackdetector/proto/l3_centralizedattackdetector_pb2_grpc.py +++ b/src/l3_distributedattackdetector/proto/l3_centralizedattackdetector_pb2_grpc.py @@ -16,13 +16,13 @@ class L3CentralizedattackdetectorStub(object): """ self.SendInput = channel.unary_unary( '/L3Centralizedattackdetector/SendInput', - request_serializer=l3__centralizedattackdetector__pb2.ModelInput.SerializeToString, + request_serializer=l3__centralizedattackdetector__pb2.L3CentralizedattackdetectorMetrics.SerializeToString, response_deserializer=l3__centralizedattackdetector__pb2.Empty.FromString, ) self.GetOutput = channel.unary_unary( '/L3Centralizedattackdetector/GetOutput', request_serializer=l3__centralizedattackdetector__pb2.Empty.SerializeToString, - response_deserializer=l3__centralizedattackdetector__pb2.ModelOutput.FromString, + response_deserializer=l3__centralizedattackdetector__pb2.L3CentralizedattackdetectorModelOutput.FromString, ) @@ -48,13 +48,13 @@ def add_L3CentralizedattackdetectorServicer_to_server(servicer, server): rpc_method_handlers = { 'SendInput': grpc.unary_unary_rpc_method_handler( servicer.SendInput, - request_deserializer=l3__centralizedattackdetector__pb2.ModelInput.FromString, + request_deserializer=l3__centralizedattackdetector__pb2.L3CentralizedattackdetectorMetrics.FromString, response_serializer=l3__centralizedattackdetector__pb2.Empty.SerializeToString, ), 'GetOutput': grpc.unary_unary_rpc_method_handler( servicer.GetOutput, request_deserializer=l3__centralizedattackdetector__pb2.Empty.FromString, - response_serializer=l3__centralizedattackdetector__pb2.ModelOutput.SerializeToString, + response_serializer=l3__centralizedattackdetector__pb2.L3CentralizedattackdetectorModelOutput.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( @@ -78,7 +78,7 @@ class L3Centralizedattackdetector(object): timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/L3Centralizedattackdetector/SendInput', - l3__centralizedattackdetector__pb2.ModelInput.SerializeToString, + l3__centralizedattackdetector__pb2.L3CentralizedattackdetectorMetrics.SerializeToString, l3__centralizedattackdetector__pb2.Empty.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @@ -96,6 +96,6 @@ class L3Centralizedattackdetector(object): metadata=None): return grpc.experimental.unary_unary(request, target, '/L3Centralizedattackdetector/GetOutput', l3__centralizedattackdetector__pb2.Empty.SerializeToString, - l3__centralizedattackdetector__pb2.ModelOutput.FromString, + l3__centralizedattackdetector__pb2.L3CentralizedattackdetectorModelOutput.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/monitoring/proto/__init__.py b/src/monitoring/proto/__init__.py index 70a332512..e69de29bb 100644 --- a/src/monitoring/proto/__init__.py +++ b/src/monitoring/proto/__init__.py @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/monitoring/proto/context_pb2.py b/src/monitoring/proto/context_pb2.py index 68602b16f..3e1b4c54d 100644 --- a/src/monitoring/proto/context_pb2.py +++ b/src/monitoring/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3703, - serialized_end=3809, + serialized_start=4307, + serialized_end=4413, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3812, - serialized_end=4009, + serialized_start=4416, + serialized_end=4613, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4012, - serialized_end=4155, + serialized_start=4616, + serialized_end=4759, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4158, - serialized_end=4287, + serialized_start=4762, + serialized_end=4891, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,12 +204,53 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4290, - serialized_end=4426, + serialized_start=4894, + serialized_end=5030, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_SLICESTATUSENUM = _descriptor.EnumDescriptor( + name='SliceStatusEnum', + full_name='context.SliceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_INIT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_ACTIVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_DEINIT', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5033, + serialized_end=5172, +) +_sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) + +SliceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SLICESTATUSENUM) _CONFIGACTIONENUM = _descriptor.EnumDescriptor( name='ConfigActionEnum', full_name='context.ConfigActionEnum', @@ -235,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4428, - serialized_end=4521, + serialized_start=5174, + serialized_end=5267, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -262,6 +303,11 @@ SERVICESTATUS_UNDEFINED = 0 SERVICESTATUS_PLANNED = 1 SERVICESTATUS_ACTIVE = 2 SERVICESTATUS_PENDING_REMOVAL = 3 +SLICESTATUS_UNDEFINED = 0 +SLICESTATUS_PLANNED = 1 +SLICESTATUS_INIT = 2 +SLICESTATUS_ACTIVE = 3 +SLICESTATUS_DEINIT = 4 CONFIGACTION_UNDEFINED = 0 CONFIGACTION_SET = 1 CONFIGACTION_DELETE = 2 @@ -1421,6 +1467,247 @@ _SERVICEEVENT = _descriptor.Descriptor( ) +_SLICEID = _descriptor.Descriptor( + name='SliceId', + full_name='context.SliceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.SliceId.context_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='slice_uuid', full_name='context.SliceId.slice_uuid', 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=2604, + serialized_end=2688, +) + + +_SLICE = _descriptor.Descriptor( + name='Slice', + full_name='context.Slice', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_id', full_name='context.Slice.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='slice_endpoint_ids', full_name='context.Slice.slice_endpoint_ids', 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='slice_constraints', full_name='context.Slice.slice_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='slice_services', full_name='context.Slice.slice_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='slice_subslice_ids', full_name='context.Slice.slice_subslice_ids', 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='slice_status', full_name='context.Slice.slice_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=2691, + serialized_end=2965, +) + + +_SLICESTATUS = _descriptor.Descriptor( + name='SliceStatus', + full_name='context.SliceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_status', full_name='context.SliceStatus.slice_status', index=0, + number=1, 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=2967, + serialized_end=3028, +) + + +_SLICEIDLIST = _descriptor.Descriptor( + name='SliceIdList', + full_name='context.SliceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_ids', full_name='context.SliceIdList.slice_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3030, + serialized_end=3080, +) + + +_SLICELIST = _descriptor.Descriptor( + name='SliceList', + full_name='context.SliceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slices', full_name='context.SliceList.slices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3082, + serialized_end=3125, +) + + +_SLICEEVENT = _descriptor.Descriptor( + name='SliceEvent', + full_name='context.SliceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.SliceEvent.event', 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='context.SliceEvent.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=3127, + serialized_end=3206, +) + + _CONNECTIONID = _descriptor.Descriptor( name='ConnectionId', full_name='context.ConnectionId', @@ -1448,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2604, - serialized_end=2658, + serialized_start=3208, + serialized_end=3262, ) @@ -1501,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2661, - serialized_end=2857, + serialized_start=3265, + serialized_end=3461, ) @@ -1533,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2859, - serialized_end=2924, + serialized_start=3463, + serialized_end=3528, ) @@ -1565,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2926, - serialized_end=2984, + serialized_start=3530, + serialized_end=3588, ) @@ -1604,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2986, - serialized_end=3080, + serialized_start=3590, + serialized_end=3684, ) @@ -1650,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3083, - serialized_end=3213, + serialized_start=3687, + serialized_end=3817, ) @@ -1696,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3216, - serialized_end=3350, + serialized_start=3820, + serialized_end=3954, ) @@ -1742,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3352, - serialized_end=3453, + serialized_start=3956, + serialized_end=4057, ) @@ -1781,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3455, - serialized_end=3518, + serialized_start=4059, + serialized_end=4122, ) @@ -1827,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3520, - serialized_end=3614, + serialized_start=4124, + serialized_end=4218, ) @@ -1866,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3616, - serialized_end=3701, + serialized_start=4220, + serialized_end=4305, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -1921,6 +2208,19 @@ _SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID _SERVICELIST.fields_by_name['services'].message_type = _SERVICE _SERVICEEVENT.fields_by_name['event'].message_type = _EVENT _SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_SLICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SLICEID.fields_by_name['slice_uuid'].message_type = _UUID +_SLICE.fields_by_name['slice_id'].message_type = _SLICEID +_SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID +_SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT +_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID +_SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS +_SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM +_SLICEIDLIST.fields_by_name['slice_ids'].message_type = _SLICEID +_SLICELIST.fields_by_name['slices'].message_type = _SLICE +_SLICEEVENT.fields_by_name['event'].message_type = _EVENT +_SLICEEVENT.fields_by_name['slice_id'].message_type = _SLICEID _CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID _CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID _CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID @@ -1969,6 +2269,12 @@ DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['SliceId'] = _SLICEID +DESCRIPTOR.message_types_by_name['Slice'] = _SLICE +DESCRIPTOR.message_types_by_name['SliceStatus'] = _SLICESTATUS +DESCRIPTOR.message_types_by_name['SliceIdList'] = _SLICEIDLIST +DESCRIPTOR.message_types_by_name['SliceList'] = _SLICELIST +DESCRIPTOR.message_types_by_name['SliceEvent'] = _SLICEEVENT DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST @@ -1985,6 +2291,7 @@ DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['SliceStatusEnum'] = _SLICESTATUSENUM DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -2205,6 +2512,48 @@ ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_messag }) _sym_db.RegisterMessage(ServiceEvent) +SliceId = _reflection.GeneratedProtocolMessageType('SliceId', (_message.Message,), { + 'DESCRIPTOR' : _SLICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceId) + }) +_sym_db.RegisterMessage(SliceId) + +Slice = _reflection.GeneratedProtocolMessageType('Slice', (_message.Message,), { + 'DESCRIPTOR' : _SLICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Slice) + }) +_sym_db.RegisterMessage(Slice) + +SliceStatus = _reflection.GeneratedProtocolMessageType('SliceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SLICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceStatus) + }) +_sym_db.RegisterMessage(SliceStatus) + +SliceIdList = _reflection.GeneratedProtocolMessageType('SliceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SLICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceIdList) + }) +_sym_db.RegisterMessage(SliceIdList) + +SliceList = _reflection.GeneratedProtocolMessageType('SliceList', (_message.Message,), { + 'DESCRIPTOR' : _SLICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceList) + }) +_sym_db.RegisterMessage(SliceList) + +SliceEvent = _reflection.GeneratedProtocolMessageType('SliceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SLICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceEvent) + }) +_sym_db.RegisterMessage(SliceEvent) + ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { 'DESCRIPTOR' : _CONNECTIONID, '__module__' : 'context_pb2' @@ -2291,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=4524, - serialized_end=6617, + serialized_start=5270, + serialized_end=7363, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/opticalattackmitigator/proto/__init__.py b/src/opticalattackmitigator/proto/__init__.py index 70a332512..e69de29bb 100644 --- a/src/opticalattackmitigator/proto/__init__.py +++ b/src/opticalattackmitigator/proto/__init__.py @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/opticalattackmitigator/proto/context_pb2.py b/src/opticalattackmitigator/proto/context_pb2.py index 68602b16f..3e1b4c54d 100644 --- a/src/opticalattackmitigator/proto/context_pb2.py +++ b/src/opticalattackmitigator/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3703, - serialized_end=3809, + serialized_start=4307, + serialized_end=4413, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3812, - serialized_end=4009, + serialized_start=4416, + serialized_end=4613, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4012, - serialized_end=4155, + serialized_start=4616, + serialized_end=4759, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4158, - serialized_end=4287, + serialized_start=4762, + serialized_end=4891, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,12 +204,53 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4290, - serialized_end=4426, + serialized_start=4894, + serialized_end=5030, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_SLICESTATUSENUM = _descriptor.EnumDescriptor( + name='SliceStatusEnum', + full_name='context.SliceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_INIT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_ACTIVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_DEINIT', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5033, + serialized_end=5172, +) +_sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) + +SliceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SLICESTATUSENUM) _CONFIGACTIONENUM = _descriptor.EnumDescriptor( name='ConfigActionEnum', full_name='context.ConfigActionEnum', @@ -235,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4428, - serialized_end=4521, + serialized_start=5174, + serialized_end=5267, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -262,6 +303,11 @@ SERVICESTATUS_UNDEFINED = 0 SERVICESTATUS_PLANNED = 1 SERVICESTATUS_ACTIVE = 2 SERVICESTATUS_PENDING_REMOVAL = 3 +SLICESTATUS_UNDEFINED = 0 +SLICESTATUS_PLANNED = 1 +SLICESTATUS_INIT = 2 +SLICESTATUS_ACTIVE = 3 +SLICESTATUS_DEINIT = 4 CONFIGACTION_UNDEFINED = 0 CONFIGACTION_SET = 1 CONFIGACTION_DELETE = 2 @@ -1421,6 +1467,247 @@ _SERVICEEVENT = _descriptor.Descriptor( ) +_SLICEID = _descriptor.Descriptor( + name='SliceId', + full_name='context.SliceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.SliceId.context_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='slice_uuid', full_name='context.SliceId.slice_uuid', 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=2604, + serialized_end=2688, +) + + +_SLICE = _descriptor.Descriptor( + name='Slice', + full_name='context.Slice', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_id', full_name='context.Slice.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='slice_endpoint_ids', full_name='context.Slice.slice_endpoint_ids', 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='slice_constraints', full_name='context.Slice.slice_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='slice_services', full_name='context.Slice.slice_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='slice_subslice_ids', full_name='context.Slice.slice_subslice_ids', 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='slice_status', full_name='context.Slice.slice_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=2691, + serialized_end=2965, +) + + +_SLICESTATUS = _descriptor.Descriptor( + name='SliceStatus', + full_name='context.SliceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_status', full_name='context.SliceStatus.slice_status', index=0, + number=1, 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=2967, + serialized_end=3028, +) + + +_SLICEIDLIST = _descriptor.Descriptor( + name='SliceIdList', + full_name='context.SliceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_ids', full_name='context.SliceIdList.slice_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3030, + serialized_end=3080, +) + + +_SLICELIST = _descriptor.Descriptor( + name='SliceList', + full_name='context.SliceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slices', full_name='context.SliceList.slices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3082, + serialized_end=3125, +) + + +_SLICEEVENT = _descriptor.Descriptor( + name='SliceEvent', + full_name='context.SliceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.SliceEvent.event', 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='context.SliceEvent.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=3127, + serialized_end=3206, +) + + _CONNECTIONID = _descriptor.Descriptor( name='ConnectionId', full_name='context.ConnectionId', @@ -1448,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2604, - serialized_end=2658, + serialized_start=3208, + serialized_end=3262, ) @@ -1501,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2661, - serialized_end=2857, + serialized_start=3265, + serialized_end=3461, ) @@ -1533,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2859, - serialized_end=2924, + serialized_start=3463, + serialized_end=3528, ) @@ -1565,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2926, - serialized_end=2984, + serialized_start=3530, + serialized_end=3588, ) @@ -1604,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2986, - serialized_end=3080, + serialized_start=3590, + serialized_end=3684, ) @@ -1650,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3083, - serialized_end=3213, + serialized_start=3687, + serialized_end=3817, ) @@ -1696,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3216, - serialized_end=3350, + serialized_start=3820, + serialized_end=3954, ) @@ -1742,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3352, - serialized_end=3453, + serialized_start=3956, + serialized_end=4057, ) @@ -1781,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3455, - serialized_end=3518, + serialized_start=4059, + serialized_end=4122, ) @@ -1827,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3520, - serialized_end=3614, + serialized_start=4124, + serialized_end=4218, ) @@ -1866,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3616, - serialized_end=3701, + serialized_start=4220, + serialized_end=4305, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -1921,6 +2208,19 @@ _SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID _SERVICELIST.fields_by_name['services'].message_type = _SERVICE _SERVICEEVENT.fields_by_name['event'].message_type = _EVENT _SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_SLICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SLICEID.fields_by_name['slice_uuid'].message_type = _UUID +_SLICE.fields_by_name['slice_id'].message_type = _SLICEID +_SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID +_SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT +_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID +_SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS +_SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM +_SLICEIDLIST.fields_by_name['slice_ids'].message_type = _SLICEID +_SLICELIST.fields_by_name['slices'].message_type = _SLICE +_SLICEEVENT.fields_by_name['event'].message_type = _EVENT +_SLICEEVENT.fields_by_name['slice_id'].message_type = _SLICEID _CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID _CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID _CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID @@ -1969,6 +2269,12 @@ DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['SliceId'] = _SLICEID +DESCRIPTOR.message_types_by_name['Slice'] = _SLICE +DESCRIPTOR.message_types_by_name['SliceStatus'] = _SLICESTATUS +DESCRIPTOR.message_types_by_name['SliceIdList'] = _SLICEIDLIST +DESCRIPTOR.message_types_by_name['SliceList'] = _SLICELIST +DESCRIPTOR.message_types_by_name['SliceEvent'] = _SLICEEVENT DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST @@ -1985,6 +2291,7 @@ DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['SliceStatusEnum'] = _SLICESTATUSENUM DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -2205,6 +2512,48 @@ ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_messag }) _sym_db.RegisterMessage(ServiceEvent) +SliceId = _reflection.GeneratedProtocolMessageType('SliceId', (_message.Message,), { + 'DESCRIPTOR' : _SLICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceId) + }) +_sym_db.RegisterMessage(SliceId) + +Slice = _reflection.GeneratedProtocolMessageType('Slice', (_message.Message,), { + 'DESCRIPTOR' : _SLICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Slice) + }) +_sym_db.RegisterMessage(Slice) + +SliceStatus = _reflection.GeneratedProtocolMessageType('SliceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SLICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceStatus) + }) +_sym_db.RegisterMessage(SliceStatus) + +SliceIdList = _reflection.GeneratedProtocolMessageType('SliceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SLICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceIdList) + }) +_sym_db.RegisterMessage(SliceIdList) + +SliceList = _reflection.GeneratedProtocolMessageType('SliceList', (_message.Message,), { + 'DESCRIPTOR' : _SLICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceList) + }) +_sym_db.RegisterMessage(SliceList) + +SliceEvent = _reflection.GeneratedProtocolMessageType('SliceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SLICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceEvent) + }) +_sym_db.RegisterMessage(SliceEvent) + ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { 'DESCRIPTOR' : _CONNECTIONID, '__module__' : 'context_pb2' @@ -2291,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=4524, - serialized_end=6617, + serialized_start=5270, + serialized_end=7363, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/opticalcentralizedattackdetector/proto/__init__.py b/src/opticalcentralizedattackdetector/proto/__init__.py index 70a332512..e69de29bb 100644 --- a/src/opticalcentralizedattackdetector/proto/__init__.py +++ b/src/opticalcentralizedattackdetector/proto/__init__.py @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/opticalcentralizedattackdetector/proto/context_pb2.py b/src/opticalcentralizedattackdetector/proto/context_pb2.py index 68602b16f..3e1b4c54d 100644 --- a/src/opticalcentralizedattackdetector/proto/context_pb2.py +++ b/src/opticalcentralizedattackdetector/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3703, - serialized_end=3809, + serialized_start=4307, + serialized_end=4413, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3812, - serialized_end=4009, + serialized_start=4416, + serialized_end=4613, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4012, - serialized_end=4155, + serialized_start=4616, + serialized_end=4759, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4158, - serialized_end=4287, + serialized_start=4762, + serialized_end=4891, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,12 +204,53 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4290, - serialized_end=4426, + serialized_start=4894, + serialized_end=5030, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_SLICESTATUSENUM = _descriptor.EnumDescriptor( + name='SliceStatusEnum', + full_name='context.SliceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_INIT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_ACTIVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_DEINIT', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5033, + serialized_end=5172, +) +_sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) + +SliceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SLICESTATUSENUM) _CONFIGACTIONENUM = _descriptor.EnumDescriptor( name='ConfigActionEnum', full_name='context.ConfigActionEnum', @@ -235,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4428, - serialized_end=4521, + serialized_start=5174, + serialized_end=5267, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -262,6 +303,11 @@ SERVICESTATUS_UNDEFINED = 0 SERVICESTATUS_PLANNED = 1 SERVICESTATUS_ACTIVE = 2 SERVICESTATUS_PENDING_REMOVAL = 3 +SLICESTATUS_UNDEFINED = 0 +SLICESTATUS_PLANNED = 1 +SLICESTATUS_INIT = 2 +SLICESTATUS_ACTIVE = 3 +SLICESTATUS_DEINIT = 4 CONFIGACTION_UNDEFINED = 0 CONFIGACTION_SET = 1 CONFIGACTION_DELETE = 2 @@ -1421,6 +1467,247 @@ _SERVICEEVENT = _descriptor.Descriptor( ) +_SLICEID = _descriptor.Descriptor( + name='SliceId', + full_name='context.SliceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.SliceId.context_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='slice_uuid', full_name='context.SliceId.slice_uuid', 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=2604, + serialized_end=2688, +) + + +_SLICE = _descriptor.Descriptor( + name='Slice', + full_name='context.Slice', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_id', full_name='context.Slice.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='slice_endpoint_ids', full_name='context.Slice.slice_endpoint_ids', 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='slice_constraints', full_name='context.Slice.slice_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='slice_services', full_name='context.Slice.slice_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='slice_subslice_ids', full_name='context.Slice.slice_subslice_ids', 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='slice_status', full_name='context.Slice.slice_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=2691, + serialized_end=2965, +) + + +_SLICESTATUS = _descriptor.Descriptor( + name='SliceStatus', + full_name='context.SliceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_status', full_name='context.SliceStatus.slice_status', index=0, + number=1, 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=2967, + serialized_end=3028, +) + + +_SLICEIDLIST = _descriptor.Descriptor( + name='SliceIdList', + full_name='context.SliceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_ids', full_name='context.SliceIdList.slice_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3030, + serialized_end=3080, +) + + +_SLICELIST = _descriptor.Descriptor( + name='SliceList', + full_name='context.SliceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slices', full_name='context.SliceList.slices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3082, + serialized_end=3125, +) + + +_SLICEEVENT = _descriptor.Descriptor( + name='SliceEvent', + full_name='context.SliceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.SliceEvent.event', 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='context.SliceEvent.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=3127, + serialized_end=3206, +) + + _CONNECTIONID = _descriptor.Descriptor( name='ConnectionId', full_name='context.ConnectionId', @@ -1448,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2604, - serialized_end=2658, + serialized_start=3208, + serialized_end=3262, ) @@ -1501,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2661, - serialized_end=2857, + serialized_start=3265, + serialized_end=3461, ) @@ -1533,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2859, - serialized_end=2924, + serialized_start=3463, + serialized_end=3528, ) @@ -1565,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2926, - serialized_end=2984, + serialized_start=3530, + serialized_end=3588, ) @@ -1604,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2986, - serialized_end=3080, + serialized_start=3590, + serialized_end=3684, ) @@ -1650,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3083, - serialized_end=3213, + serialized_start=3687, + serialized_end=3817, ) @@ -1696,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3216, - serialized_end=3350, + serialized_start=3820, + serialized_end=3954, ) @@ -1742,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3352, - serialized_end=3453, + serialized_start=3956, + serialized_end=4057, ) @@ -1781,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3455, - serialized_end=3518, + serialized_start=4059, + serialized_end=4122, ) @@ -1827,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3520, - serialized_end=3614, + serialized_start=4124, + serialized_end=4218, ) @@ -1866,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3616, - serialized_end=3701, + serialized_start=4220, + serialized_end=4305, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -1921,6 +2208,19 @@ _SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID _SERVICELIST.fields_by_name['services'].message_type = _SERVICE _SERVICEEVENT.fields_by_name['event'].message_type = _EVENT _SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_SLICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SLICEID.fields_by_name['slice_uuid'].message_type = _UUID +_SLICE.fields_by_name['slice_id'].message_type = _SLICEID +_SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID +_SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT +_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID +_SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS +_SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM +_SLICEIDLIST.fields_by_name['slice_ids'].message_type = _SLICEID +_SLICELIST.fields_by_name['slices'].message_type = _SLICE +_SLICEEVENT.fields_by_name['event'].message_type = _EVENT +_SLICEEVENT.fields_by_name['slice_id'].message_type = _SLICEID _CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID _CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID _CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID @@ -1969,6 +2269,12 @@ DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['SliceId'] = _SLICEID +DESCRIPTOR.message_types_by_name['Slice'] = _SLICE +DESCRIPTOR.message_types_by_name['SliceStatus'] = _SLICESTATUS +DESCRIPTOR.message_types_by_name['SliceIdList'] = _SLICEIDLIST +DESCRIPTOR.message_types_by_name['SliceList'] = _SLICELIST +DESCRIPTOR.message_types_by_name['SliceEvent'] = _SLICEEVENT DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST @@ -1985,6 +2291,7 @@ DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['SliceStatusEnum'] = _SLICESTATUSENUM DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -2205,6 +2512,48 @@ ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_messag }) _sym_db.RegisterMessage(ServiceEvent) +SliceId = _reflection.GeneratedProtocolMessageType('SliceId', (_message.Message,), { + 'DESCRIPTOR' : _SLICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceId) + }) +_sym_db.RegisterMessage(SliceId) + +Slice = _reflection.GeneratedProtocolMessageType('Slice', (_message.Message,), { + 'DESCRIPTOR' : _SLICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Slice) + }) +_sym_db.RegisterMessage(Slice) + +SliceStatus = _reflection.GeneratedProtocolMessageType('SliceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SLICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceStatus) + }) +_sym_db.RegisterMessage(SliceStatus) + +SliceIdList = _reflection.GeneratedProtocolMessageType('SliceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SLICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceIdList) + }) +_sym_db.RegisterMessage(SliceIdList) + +SliceList = _reflection.GeneratedProtocolMessageType('SliceList', (_message.Message,), { + 'DESCRIPTOR' : _SLICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceList) + }) +_sym_db.RegisterMessage(SliceList) + +SliceEvent = _reflection.GeneratedProtocolMessageType('SliceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SLICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceEvent) + }) +_sym_db.RegisterMessage(SliceEvent) + ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { 'DESCRIPTOR' : _CONNECTIONID, '__module__' : 'context_pb2' @@ -2291,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=4524, - serialized_end=6617, + serialized_start=5270, + serialized_end=7363, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/opticalcentralizedattackdetector/proto/service_pb2.py b/src/opticalcentralizedattackdetector/proto/service_pb2.py index 7a006915b..8e2806c76 100644 --- a/src/opticalcentralizedattackdetector/proto/service_pb2.py +++ b/src/opticalcentralizedattackdetector/proto/service_pb2.py @@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xfd\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12\x42\n\x11GetConnectionList\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x62\x06proto3' + serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xb9\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x62\x06proto3' , dependencies=[context__pb2.DESCRIPTOR,]) @@ -38,7 +38,7 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=42, - serialized_end=295, + serialized_end=227, methods=[ _descriptor.MethodDescriptor( name='CreateService', @@ -70,16 +70,6 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), - _descriptor.MethodDescriptor( - name='GetConnectionList', - full_name='service.ServiceService.GetConnectionList', - index=3, - containing_service=None, - input_type=context__pb2._SERVICEID, - output_type=context__pb2._CONNECTIONLIST, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), ]) _sym_db.RegisterServiceDescriptor(_SERVICESERVICE) diff --git a/src/service/proto/__init__.py b/src/service/proto/__init__.py index 70a332512..e69de29bb 100644 --- a/src/service/proto/__init__.py +++ b/src/service/proto/__init__.py @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/service/proto/context_pb2.py b/src/service/proto/context_pb2.py index 68602b16f..3e1b4c54d 100644 --- a/src/service/proto/context_pb2.py +++ b/src/service/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3703, - serialized_end=3809, + serialized_start=4307, + serialized_end=4413, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3812, - serialized_end=4009, + serialized_start=4416, + serialized_end=4613, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4012, - serialized_end=4155, + serialized_start=4616, + serialized_end=4759, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4158, - serialized_end=4287, + serialized_start=4762, + serialized_end=4891, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,12 +204,53 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4290, - serialized_end=4426, + serialized_start=4894, + serialized_end=5030, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_SLICESTATUSENUM = _descriptor.EnumDescriptor( + name='SliceStatusEnum', + full_name='context.SliceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_INIT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_ACTIVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_DEINIT', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5033, + serialized_end=5172, +) +_sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) + +SliceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SLICESTATUSENUM) _CONFIGACTIONENUM = _descriptor.EnumDescriptor( name='ConfigActionEnum', full_name='context.ConfigActionEnum', @@ -235,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4428, - serialized_end=4521, + serialized_start=5174, + serialized_end=5267, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -262,6 +303,11 @@ SERVICESTATUS_UNDEFINED = 0 SERVICESTATUS_PLANNED = 1 SERVICESTATUS_ACTIVE = 2 SERVICESTATUS_PENDING_REMOVAL = 3 +SLICESTATUS_UNDEFINED = 0 +SLICESTATUS_PLANNED = 1 +SLICESTATUS_INIT = 2 +SLICESTATUS_ACTIVE = 3 +SLICESTATUS_DEINIT = 4 CONFIGACTION_UNDEFINED = 0 CONFIGACTION_SET = 1 CONFIGACTION_DELETE = 2 @@ -1421,6 +1467,247 @@ _SERVICEEVENT = _descriptor.Descriptor( ) +_SLICEID = _descriptor.Descriptor( + name='SliceId', + full_name='context.SliceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.SliceId.context_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='slice_uuid', full_name='context.SliceId.slice_uuid', 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=2604, + serialized_end=2688, +) + + +_SLICE = _descriptor.Descriptor( + name='Slice', + full_name='context.Slice', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_id', full_name='context.Slice.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='slice_endpoint_ids', full_name='context.Slice.slice_endpoint_ids', 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='slice_constraints', full_name='context.Slice.slice_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='slice_services', full_name='context.Slice.slice_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='slice_subslice_ids', full_name='context.Slice.slice_subslice_ids', 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='slice_status', full_name='context.Slice.slice_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=2691, + serialized_end=2965, +) + + +_SLICESTATUS = _descriptor.Descriptor( + name='SliceStatus', + full_name='context.SliceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_status', full_name='context.SliceStatus.slice_status', index=0, + number=1, 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=2967, + serialized_end=3028, +) + + +_SLICEIDLIST = _descriptor.Descriptor( + name='SliceIdList', + full_name='context.SliceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_ids', full_name='context.SliceIdList.slice_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3030, + serialized_end=3080, +) + + +_SLICELIST = _descriptor.Descriptor( + name='SliceList', + full_name='context.SliceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slices', full_name='context.SliceList.slices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3082, + serialized_end=3125, +) + + +_SLICEEVENT = _descriptor.Descriptor( + name='SliceEvent', + full_name='context.SliceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.SliceEvent.event', 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='context.SliceEvent.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=3127, + serialized_end=3206, +) + + _CONNECTIONID = _descriptor.Descriptor( name='ConnectionId', full_name='context.ConnectionId', @@ -1448,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2604, - serialized_end=2658, + serialized_start=3208, + serialized_end=3262, ) @@ -1501,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2661, - serialized_end=2857, + serialized_start=3265, + serialized_end=3461, ) @@ -1533,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2859, - serialized_end=2924, + serialized_start=3463, + serialized_end=3528, ) @@ -1565,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2926, - serialized_end=2984, + serialized_start=3530, + serialized_end=3588, ) @@ -1604,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2986, - serialized_end=3080, + serialized_start=3590, + serialized_end=3684, ) @@ -1650,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3083, - serialized_end=3213, + serialized_start=3687, + serialized_end=3817, ) @@ -1696,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3216, - serialized_end=3350, + serialized_start=3820, + serialized_end=3954, ) @@ -1742,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3352, - serialized_end=3453, + serialized_start=3956, + serialized_end=4057, ) @@ -1781,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3455, - serialized_end=3518, + serialized_start=4059, + serialized_end=4122, ) @@ -1827,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3520, - serialized_end=3614, + serialized_start=4124, + serialized_end=4218, ) @@ -1866,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3616, - serialized_end=3701, + serialized_start=4220, + serialized_end=4305, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -1921,6 +2208,19 @@ _SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID _SERVICELIST.fields_by_name['services'].message_type = _SERVICE _SERVICEEVENT.fields_by_name['event'].message_type = _EVENT _SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_SLICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SLICEID.fields_by_name['slice_uuid'].message_type = _UUID +_SLICE.fields_by_name['slice_id'].message_type = _SLICEID +_SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID +_SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT +_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID +_SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS +_SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM +_SLICEIDLIST.fields_by_name['slice_ids'].message_type = _SLICEID +_SLICELIST.fields_by_name['slices'].message_type = _SLICE +_SLICEEVENT.fields_by_name['event'].message_type = _EVENT +_SLICEEVENT.fields_by_name['slice_id'].message_type = _SLICEID _CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID _CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID _CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID @@ -1969,6 +2269,12 @@ DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['SliceId'] = _SLICEID +DESCRIPTOR.message_types_by_name['Slice'] = _SLICE +DESCRIPTOR.message_types_by_name['SliceStatus'] = _SLICESTATUS +DESCRIPTOR.message_types_by_name['SliceIdList'] = _SLICEIDLIST +DESCRIPTOR.message_types_by_name['SliceList'] = _SLICELIST +DESCRIPTOR.message_types_by_name['SliceEvent'] = _SLICEEVENT DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST @@ -1985,6 +2291,7 @@ DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['SliceStatusEnum'] = _SLICESTATUSENUM DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -2205,6 +2512,48 @@ ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_messag }) _sym_db.RegisterMessage(ServiceEvent) +SliceId = _reflection.GeneratedProtocolMessageType('SliceId', (_message.Message,), { + 'DESCRIPTOR' : _SLICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceId) + }) +_sym_db.RegisterMessage(SliceId) + +Slice = _reflection.GeneratedProtocolMessageType('Slice', (_message.Message,), { + 'DESCRIPTOR' : _SLICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Slice) + }) +_sym_db.RegisterMessage(Slice) + +SliceStatus = _reflection.GeneratedProtocolMessageType('SliceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SLICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceStatus) + }) +_sym_db.RegisterMessage(SliceStatus) + +SliceIdList = _reflection.GeneratedProtocolMessageType('SliceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SLICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceIdList) + }) +_sym_db.RegisterMessage(SliceIdList) + +SliceList = _reflection.GeneratedProtocolMessageType('SliceList', (_message.Message,), { + 'DESCRIPTOR' : _SLICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceList) + }) +_sym_db.RegisterMessage(SliceList) + +SliceEvent = _reflection.GeneratedProtocolMessageType('SliceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SLICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceEvent) + }) +_sym_db.RegisterMessage(SliceEvent) + ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { 'DESCRIPTOR' : _CONNECTIONID, '__module__' : 'context_pb2' @@ -2291,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=4524, - serialized_end=6617, + serialized_start=5270, + serialized_end=7363, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/service/proto/service_pb2.py b/src/service/proto/service_pb2.py index 7a006915b..8e2806c76 100644 --- a/src/service/proto/service_pb2.py +++ b/src/service/proto/service_pb2.py @@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xfd\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12\x42\n\x11GetConnectionList\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x62\x06proto3' + serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xb9\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x62\x06proto3' , dependencies=[context__pb2.DESCRIPTOR,]) @@ -38,7 +38,7 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=42, - serialized_end=295, + serialized_end=227, methods=[ _descriptor.MethodDescriptor( name='CreateService', @@ -70,16 +70,6 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), - _descriptor.MethodDescriptor( - name='GetConnectionList', - full_name='service.ServiceService.GetConnectionList', - index=3, - containing_service=None, - input_type=context__pb2._SERVICEID, - output_type=context__pb2._CONNECTIONLIST, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), ]) _sym_db.RegisterServiceDescriptor(_SERVICESERVICE) diff --git a/src/service/proto/service_pb2_grpc.py b/src/service/proto/service_pb2_grpc.py index 58cd47e93..7269e1f5f 100644 --- a/src/service/proto/service_pb2_grpc.py +++ b/src/service/proto/service_pb2_grpc.py @@ -29,11 +29,6 @@ class ServiceServiceStub(object): request_serializer=context__pb2.ServiceId.SerializeToString, response_deserializer=context__pb2.Empty.FromString, ) - self.GetConnectionList = channel.unary_unary( - '/service.ServiceService/GetConnectionList', - request_serializer=context__pb2.ServiceId.SerializeToString, - response_deserializer=context__pb2.ConnectionList.FromString, - ) class ServiceServiceServicer(object): @@ -57,12 +52,6 @@ class ServiceServiceServicer(object): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def GetConnectionList(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_ServiceServiceServicer_to_server(servicer, server): rpc_method_handlers = { @@ -81,11 +70,6 @@ def add_ServiceServiceServicer_to_server(servicer, server): request_deserializer=context__pb2.ServiceId.FromString, response_serializer=context__pb2.Empty.SerializeToString, ), - 'GetConnectionList': grpc.unary_unary_rpc_method_handler( - servicer.GetConnectionList, - request_deserializer=context__pb2.ServiceId.FromString, - response_serializer=context__pb2.ConnectionList.SerializeToString, - ), } generic_handler = grpc.method_handlers_generic_handler( 'service.ServiceService', rpc_method_handlers) @@ -146,20 +130,3 @@ class ServiceService(object): context__pb2.Empty.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) - - @staticmethod - def GetConnectionList(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, '/service.ServiceService/GetConnectionList', - context__pb2.ServiceId.SerializeToString, - context__pb2.ConnectionList.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/slice/proto/context_pb2.py b/src/slice/proto/context_pb2.py index 68602b16f..3e1b4c54d 100644 --- a/src/slice/proto/context_pb2.py +++ b/src/slice/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3703, - serialized_end=3809, + serialized_start=4307, + serialized_end=4413, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3812, - serialized_end=4009, + serialized_start=4416, + serialized_end=4613, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4012, - serialized_end=4155, + serialized_start=4616, + serialized_end=4759, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4158, - serialized_end=4287, + serialized_start=4762, + serialized_end=4891, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,12 +204,53 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4290, - serialized_end=4426, + serialized_start=4894, + serialized_end=5030, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_SLICESTATUSENUM = _descriptor.EnumDescriptor( + name='SliceStatusEnum', + full_name='context.SliceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_INIT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_ACTIVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_DEINIT', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5033, + serialized_end=5172, +) +_sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) + +SliceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SLICESTATUSENUM) _CONFIGACTIONENUM = _descriptor.EnumDescriptor( name='ConfigActionEnum', full_name='context.ConfigActionEnum', @@ -235,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4428, - serialized_end=4521, + serialized_start=5174, + serialized_end=5267, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -262,6 +303,11 @@ SERVICESTATUS_UNDEFINED = 0 SERVICESTATUS_PLANNED = 1 SERVICESTATUS_ACTIVE = 2 SERVICESTATUS_PENDING_REMOVAL = 3 +SLICESTATUS_UNDEFINED = 0 +SLICESTATUS_PLANNED = 1 +SLICESTATUS_INIT = 2 +SLICESTATUS_ACTIVE = 3 +SLICESTATUS_DEINIT = 4 CONFIGACTION_UNDEFINED = 0 CONFIGACTION_SET = 1 CONFIGACTION_DELETE = 2 @@ -1421,6 +1467,247 @@ _SERVICEEVENT = _descriptor.Descriptor( ) +_SLICEID = _descriptor.Descriptor( + name='SliceId', + full_name='context.SliceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.SliceId.context_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='slice_uuid', full_name='context.SliceId.slice_uuid', 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=2604, + serialized_end=2688, +) + + +_SLICE = _descriptor.Descriptor( + name='Slice', + full_name='context.Slice', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_id', full_name='context.Slice.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='slice_endpoint_ids', full_name='context.Slice.slice_endpoint_ids', 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='slice_constraints', full_name='context.Slice.slice_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='slice_services', full_name='context.Slice.slice_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='slice_subslice_ids', full_name='context.Slice.slice_subslice_ids', 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='slice_status', full_name='context.Slice.slice_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=2691, + serialized_end=2965, +) + + +_SLICESTATUS = _descriptor.Descriptor( + name='SliceStatus', + full_name='context.SliceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_status', full_name='context.SliceStatus.slice_status', index=0, + number=1, 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=2967, + serialized_end=3028, +) + + +_SLICEIDLIST = _descriptor.Descriptor( + name='SliceIdList', + full_name='context.SliceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_ids', full_name='context.SliceIdList.slice_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3030, + serialized_end=3080, +) + + +_SLICELIST = _descriptor.Descriptor( + name='SliceList', + full_name='context.SliceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slices', full_name='context.SliceList.slices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3082, + serialized_end=3125, +) + + +_SLICEEVENT = _descriptor.Descriptor( + name='SliceEvent', + full_name='context.SliceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.SliceEvent.event', 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='context.SliceEvent.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=3127, + serialized_end=3206, +) + + _CONNECTIONID = _descriptor.Descriptor( name='ConnectionId', full_name='context.ConnectionId', @@ -1448,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2604, - serialized_end=2658, + serialized_start=3208, + serialized_end=3262, ) @@ -1501,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2661, - serialized_end=2857, + serialized_start=3265, + serialized_end=3461, ) @@ -1533,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2859, - serialized_end=2924, + serialized_start=3463, + serialized_end=3528, ) @@ -1565,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2926, - serialized_end=2984, + serialized_start=3530, + serialized_end=3588, ) @@ -1604,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2986, - serialized_end=3080, + serialized_start=3590, + serialized_end=3684, ) @@ -1650,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3083, - serialized_end=3213, + serialized_start=3687, + serialized_end=3817, ) @@ -1696,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3216, - serialized_end=3350, + serialized_start=3820, + serialized_end=3954, ) @@ -1742,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3352, - serialized_end=3453, + serialized_start=3956, + serialized_end=4057, ) @@ -1781,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3455, - serialized_end=3518, + serialized_start=4059, + serialized_end=4122, ) @@ -1827,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3520, - serialized_end=3614, + serialized_start=4124, + serialized_end=4218, ) @@ -1866,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3616, - serialized_end=3701, + serialized_start=4220, + serialized_end=4305, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -1921,6 +2208,19 @@ _SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID _SERVICELIST.fields_by_name['services'].message_type = _SERVICE _SERVICEEVENT.fields_by_name['event'].message_type = _EVENT _SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_SLICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SLICEID.fields_by_name['slice_uuid'].message_type = _UUID +_SLICE.fields_by_name['slice_id'].message_type = _SLICEID +_SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID +_SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT +_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID +_SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS +_SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM +_SLICEIDLIST.fields_by_name['slice_ids'].message_type = _SLICEID +_SLICELIST.fields_by_name['slices'].message_type = _SLICE +_SLICEEVENT.fields_by_name['event'].message_type = _EVENT +_SLICEEVENT.fields_by_name['slice_id'].message_type = _SLICEID _CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID _CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID _CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID @@ -1969,6 +2269,12 @@ DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['SliceId'] = _SLICEID +DESCRIPTOR.message_types_by_name['Slice'] = _SLICE +DESCRIPTOR.message_types_by_name['SliceStatus'] = _SLICESTATUS +DESCRIPTOR.message_types_by_name['SliceIdList'] = _SLICEIDLIST +DESCRIPTOR.message_types_by_name['SliceList'] = _SLICELIST +DESCRIPTOR.message_types_by_name['SliceEvent'] = _SLICEEVENT DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST @@ -1985,6 +2291,7 @@ DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['SliceStatusEnum'] = _SLICESTATUSENUM DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -2205,6 +2512,48 @@ ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_messag }) _sym_db.RegisterMessage(ServiceEvent) +SliceId = _reflection.GeneratedProtocolMessageType('SliceId', (_message.Message,), { + 'DESCRIPTOR' : _SLICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceId) + }) +_sym_db.RegisterMessage(SliceId) + +Slice = _reflection.GeneratedProtocolMessageType('Slice', (_message.Message,), { + 'DESCRIPTOR' : _SLICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Slice) + }) +_sym_db.RegisterMessage(Slice) + +SliceStatus = _reflection.GeneratedProtocolMessageType('SliceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SLICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceStatus) + }) +_sym_db.RegisterMessage(SliceStatus) + +SliceIdList = _reflection.GeneratedProtocolMessageType('SliceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SLICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceIdList) + }) +_sym_db.RegisterMessage(SliceIdList) + +SliceList = _reflection.GeneratedProtocolMessageType('SliceList', (_message.Message,), { + 'DESCRIPTOR' : _SLICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceList) + }) +_sym_db.RegisterMessage(SliceList) + +SliceEvent = _reflection.GeneratedProtocolMessageType('SliceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SLICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceEvent) + }) +_sym_db.RegisterMessage(SliceEvent) + ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { 'DESCRIPTOR' : _CONNECTIONID, '__module__' : 'context_pb2' @@ -2291,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=4524, - serialized_end=6617, + serialized_start=5270, + serialized_end=7363, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/slice/proto/slice_pb2.py b/src/slice/proto/slice_pb2.py index 91dbaaae3..ac8b165db 100644 --- a/src/slice/proto/slice_pb2.py +++ b/src/slice/proto/slice_pb2.py @@ -2,7 +2,6 @@ # 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 @@ -21,275 +20,14 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x0bslice.proto\x12\x05slice\x1a\rcontext.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.context.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' + serialized_pb=b'\n\x0bslice.proto\x12\x05slice\x1a\rcontext.proto2x\n\x0cSliceService\x12\x37\n\x11\x43reateUpdateSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12/\n\x0b\x44\x65leteSlice\x12\x0e.context.Slice\x1a\x0e.context.Empty\"\x00\x62\x06proto3' , dependencies=[context__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=509, - serialized_end=573, -) -_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=37, - serialized_end=88, -) - - -_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=91, - serialized_end=335, -) -_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=337, - serialized_end=418, -) - - -_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=420, - serialized_end=507, -) - -_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 = context__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( @@ -299,16 +37,16 @@ _SLICESERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=576, - serialized_end=712, + serialized_start=37, + serialized_end=157, methods=[ _descriptor.MethodDescriptor( name='CreateUpdateSlice', full_name='slice.SliceService.CreateUpdateSlice', index=0, containing_service=None, - input_type=_TRANSPORTSLICE, - output_type=_SLICESTATUS, + input_type=context__pb2._SLICE, + output_type=context__pb2._SLICEID, serialized_options=None, create_key=_descriptor._internal_create_key, ), @@ -317,7 +55,7 @@ _SLICESERVICE = _descriptor.ServiceDescriptor( full_name='slice.SliceService.DeleteSlice', index=1, containing_service=None, - input_type=_TRANSPORTSLICE, + input_type=context__pb2._SLICE, output_type=context__pb2._EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, diff --git a/src/slice/proto/slice_pb2_grpc.py b/src/slice/proto/slice_pb2_grpc.py index e7762a910..bcd736689 100644 --- a/src/slice/proto/slice_pb2_grpc.py +++ b/src/slice/proto/slice_pb2_grpc.py @@ -3,7 +3,6 @@ import grpc from . import context_pb2 as context__pb2 -from . import slice_pb2 as slice__pb2 class SliceServiceStub(object): @@ -17,12 +16,12 @@ class SliceServiceStub(object): """ self.CreateUpdateSlice = channel.unary_unary( '/slice.SliceService/CreateUpdateSlice', - request_serializer=slice__pb2.TransportSlice.SerializeToString, - response_deserializer=slice__pb2.SliceStatus.FromString, + request_serializer=context__pb2.Slice.SerializeToString, + response_deserializer=context__pb2.SliceId.FromString, ) self.DeleteSlice = channel.unary_unary( '/slice.SliceService/DeleteSlice', - request_serializer=slice__pb2.TransportSlice.SerializeToString, + request_serializer=context__pb2.Slice.SerializeToString, response_deserializer=context__pb2.Empty.FromString, ) @@ -47,12 +46,12 @@ 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, + request_deserializer=context__pb2.Slice.FromString, + response_serializer=context__pb2.SliceId.SerializeToString, ), 'DeleteSlice': grpc.unary_unary_rpc_method_handler( servicer.DeleteSlice, - request_deserializer=slice__pb2.TransportSlice.FromString, + request_deserializer=context__pb2.Slice.FromString, response_serializer=context__pb2.Empty.SerializeToString, ), } @@ -77,8 +76,8 @@ class SliceService(object): timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/slice.SliceService/CreateUpdateSlice', - slice__pb2.TransportSlice.SerializeToString, - slice__pb2.SliceStatus.FromString, + context__pb2.Slice.SerializeToString, + context__pb2.SliceId.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @@ -94,7 +93,7 @@ class SliceService(object): timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/slice.SliceService/DeleteSlice', - slice__pb2.TransportSlice.SerializeToString, + context__pb2.Slice.SerializeToString, context__pb2.Empty.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/src/webui/proto/__init__.py b/src/webui/proto/__init__.py index 70a332512..e69de29bb 100644 --- a/src/webui/proto/__init__.py +++ b/src/webui/proto/__init__.py @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/webui/proto/context_pb2.py b/src/webui/proto/context_pb2.py index 68602b16f..3e1b4c54d 100644 --- a/src/webui/proto/context_pb2.py +++ b/src/webui/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3703, - serialized_end=3809, + serialized_start=4307, + serialized_end=4413, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=3812, - serialized_end=4009, + serialized_start=4416, + serialized_end=4613, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4012, - serialized_end=4155, + serialized_start=4616, + serialized_end=4759, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4158, - serialized_end=4287, + serialized_start=4762, + serialized_end=4891, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,12 +204,53 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4290, - serialized_end=4426, + serialized_start=4894, + serialized_end=5030, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_SLICESTATUSENUM = _descriptor.EnumDescriptor( + name='SliceStatusEnum', + full_name='context.SliceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_INIT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_ACTIVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_DEINIT', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5033, + serialized_end=5172, +) +_sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) + +SliceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SLICESTATUSENUM) _CONFIGACTIONENUM = _descriptor.EnumDescriptor( name='ConfigActionEnum', full_name='context.ConfigActionEnum', @@ -235,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4428, - serialized_end=4521, + serialized_start=5174, + serialized_end=5267, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -262,6 +303,11 @@ SERVICESTATUS_UNDEFINED = 0 SERVICESTATUS_PLANNED = 1 SERVICESTATUS_ACTIVE = 2 SERVICESTATUS_PENDING_REMOVAL = 3 +SLICESTATUS_UNDEFINED = 0 +SLICESTATUS_PLANNED = 1 +SLICESTATUS_INIT = 2 +SLICESTATUS_ACTIVE = 3 +SLICESTATUS_DEINIT = 4 CONFIGACTION_UNDEFINED = 0 CONFIGACTION_SET = 1 CONFIGACTION_DELETE = 2 @@ -1421,6 +1467,247 @@ _SERVICEEVENT = _descriptor.Descriptor( ) +_SLICEID = _descriptor.Descriptor( + name='SliceId', + full_name='context.SliceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.SliceId.context_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='slice_uuid', full_name='context.SliceId.slice_uuid', 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=2604, + serialized_end=2688, +) + + +_SLICE = _descriptor.Descriptor( + name='Slice', + full_name='context.Slice', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_id', full_name='context.Slice.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='slice_endpoint_ids', full_name='context.Slice.slice_endpoint_ids', 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='slice_constraints', full_name='context.Slice.slice_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='slice_services', full_name='context.Slice.slice_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='slice_subslice_ids', full_name='context.Slice.slice_subslice_ids', 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='slice_status', full_name='context.Slice.slice_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=2691, + serialized_end=2965, +) + + +_SLICESTATUS = _descriptor.Descriptor( + name='SliceStatus', + full_name='context.SliceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_status', full_name='context.SliceStatus.slice_status', index=0, + number=1, 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=2967, + serialized_end=3028, +) + + +_SLICEIDLIST = _descriptor.Descriptor( + name='SliceIdList', + full_name='context.SliceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_ids', full_name='context.SliceIdList.slice_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3030, + serialized_end=3080, +) + + +_SLICELIST = _descriptor.Descriptor( + name='SliceList', + full_name='context.SliceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slices', full_name='context.SliceList.slices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3082, + serialized_end=3125, +) + + +_SLICEEVENT = _descriptor.Descriptor( + name='SliceEvent', + full_name='context.SliceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.SliceEvent.event', 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='context.SliceEvent.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=3127, + serialized_end=3206, +) + + _CONNECTIONID = _descriptor.Descriptor( name='ConnectionId', full_name='context.ConnectionId', @@ -1448,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2604, - serialized_end=2658, + serialized_start=3208, + serialized_end=3262, ) @@ -1501,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2661, - serialized_end=2857, + serialized_start=3265, + serialized_end=3461, ) @@ -1533,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2859, - serialized_end=2924, + serialized_start=3463, + serialized_end=3528, ) @@ -1565,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2926, - serialized_end=2984, + serialized_start=3530, + serialized_end=3588, ) @@ -1604,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2986, - serialized_end=3080, + serialized_start=3590, + serialized_end=3684, ) @@ -1650,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3083, - serialized_end=3213, + serialized_start=3687, + serialized_end=3817, ) @@ -1696,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3216, - serialized_end=3350, + serialized_start=3820, + serialized_end=3954, ) @@ -1742,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3352, - serialized_end=3453, + serialized_start=3956, + serialized_end=4057, ) @@ -1781,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3455, - serialized_end=3518, + serialized_start=4059, + serialized_end=4122, ) @@ -1827,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3520, - serialized_end=3614, + serialized_start=4124, + serialized_end=4218, ) @@ -1866,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3616, - serialized_end=3701, + serialized_start=4220, + serialized_end=4305, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -1921,6 +2208,19 @@ _SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID _SERVICELIST.fields_by_name['services'].message_type = _SERVICE _SERVICEEVENT.fields_by_name['event'].message_type = _EVENT _SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_SLICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SLICEID.fields_by_name['slice_uuid'].message_type = _UUID +_SLICE.fields_by_name['slice_id'].message_type = _SLICEID +_SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID +_SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT +_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID +_SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS +_SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM +_SLICEIDLIST.fields_by_name['slice_ids'].message_type = _SLICEID +_SLICELIST.fields_by_name['slices'].message_type = _SLICE +_SLICEEVENT.fields_by_name['event'].message_type = _EVENT +_SLICEEVENT.fields_by_name['slice_id'].message_type = _SLICEID _CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID _CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID _CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID @@ -1969,6 +2269,12 @@ DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['SliceId'] = _SLICEID +DESCRIPTOR.message_types_by_name['Slice'] = _SLICE +DESCRIPTOR.message_types_by_name['SliceStatus'] = _SLICESTATUS +DESCRIPTOR.message_types_by_name['SliceIdList'] = _SLICEIDLIST +DESCRIPTOR.message_types_by_name['SliceList'] = _SLICELIST +DESCRIPTOR.message_types_by_name['SliceEvent'] = _SLICEEVENT DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST @@ -1985,6 +2291,7 @@ DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['SliceStatusEnum'] = _SLICESTATUSENUM DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM _sym_db.RegisterFileDescriptor(DESCRIPTOR) @@ -2205,6 +2512,48 @@ ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_messag }) _sym_db.RegisterMessage(ServiceEvent) +SliceId = _reflection.GeneratedProtocolMessageType('SliceId', (_message.Message,), { + 'DESCRIPTOR' : _SLICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceId) + }) +_sym_db.RegisterMessage(SliceId) + +Slice = _reflection.GeneratedProtocolMessageType('Slice', (_message.Message,), { + 'DESCRIPTOR' : _SLICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Slice) + }) +_sym_db.RegisterMessage(Slice) + +SliceStatus = _reflection.GeneratedProtocolMessageType('SliceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SLICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceStatus) + }) +_sym_db.RegisterMessage(SliceStatus) + +SliceIdList = _reflection.GeneratedProtocolMessageType('SliceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SLICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceIdList) + }) +_sym_db.RegisterMessage(SliceIdList) + +SliceList = _reflection.GeneratedProtocolMessageType('SliceList', (_message.Message,), { + 'DESCRIPTOR' : _SLICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceList) + }) +_sym_db.RegisterMessage(SliceList) + +SliceEvent = _reflection.GeneratedProtocolMessageType('SliceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SLICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceEvent) + }) +_sym_db.RegisterMessage(SliceEvent) + ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { 'DESCRIPTOR' : _CONNECTIONID, '__module__' : 'context_pb2' @@ -2291,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=4524, - serialized_end=6617, + serialized_start=5270, + serialized_end=7363, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/webui/proto/service_pb2.py b/src/webui/proto/service_pb2.py index 7a006915b..8e2806c76 100644 --- a/src/webui/proto/service_pb2.py +++ b/src/webui/proto/service_pb2.py @@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xfd\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12\x42\n\x11GetConnectionList\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x62\x06proto3' + serialized_pb=b'\n\rservice.proto\x12\x07service\x1a\rcontext.proto2\xb9\x01\n\x0eServiceService\x12\x37\n\rCreateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x37\n\rUpdateService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rDeleteService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x62\x06proto3' , dependencies=[context__pb2.DESCRIPTOR,]) @@ -38,7 +38,7 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=42, - serialized_end=295, + serialized_end=227, methods=[ _descriptor.MethodDescriptor( name='CreateService', @@ -70,16 +70,6 @@ _SERVICESERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), - _descriptor.MethodDescriptor( - name='GetConnectionList', - full_name='service.ServiceService.GetConnectionList', - index=3, - containing_service=None, - input_type=context__pb2._SERVICEID, - output_type=context__pb2._CONNECTIONLIST, - serialized_options=None, - create_key=_descriptor._internal_create_key, - ), ]) _sym_db.RegisterServiceDescriptor(_SERVICESERVICE) -- GitLab From 3970db8feb8b6467a2963e5bfb25c0fd5ba3a52c Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Thu, 10 Mar 2022 14:35:55 +0100 Subject: [PATCH 012/353] Corrected interdomain and slice component manifest files --- manifests/interdomainservice.yaml | 2 +- manifests/sliceservice.yaml | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/manifests/interdomainservice.yaml b/manifests/interdomainservice.yaml index 6a107eec2..ca30da010 100644 --- a/manifests/interdomainservice.yaml +++ b/manifests/interdomainservice.yaml @@ -34,7 +34,7 @@ spec: - containerPort: 10010 env: - name: LOG_LEVEL - value: "INFO" + value: "DEBUG" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:10010"] diff --git a/manifests/sliceservice.yaml b/manifests/sliceservice.yaml index 6cd81e61a..5c4b8855f 100644 --- a/manifests/sliceservice.yaml +++ b/manifests/sliceservice.yaml @@ -19,10 +19,6 @@ spec: ports: - containerPort: 4040 env: - - name: DB_ENGINE - value: "redis" - - name: REDIS_DATABASE_ID - value: "0" - name: LOG_LEVEL value: "DEBUG" readinessProbe: -- GitLab From 586c8c30688f984756ea5a27bb49b90afdf14c68 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Thu, 10 Mar 2022 14:37:27 +0100 Subject: [PATCH 013/353] OECC/PSC'22 functional test: - defined test objects (contexts, topologies, devices, links, service) - updated bootstrap test script - updated create service test script - updated deploy script --- src/tests/oeccpsc22/deploy_in_kubernetes.sh | 2 +- src/tests/oeccpsc22/run_test_01_bootstrap.sh | 4 + .../oeccpsc22/run_test_02_create_service.sh | 22 ++- .../oeccpsc22/run_test_03_delete_service.sh | 22 ++- src/tests/oeccpsc22/run_test_04_cleanup.sh | 22 ++- src/tests/oeccpsc22/tests/Objects_Domain_1.py | 119 ++++++++------- src/tests/oeccpsc22/tests/Objects_Domain_2.py | 119 ++++++++------- src/tests/oeccpsc22/tests/Objects_Service.py | 46 +++--- .../tests/test_functional_bootstrap.py | 4 - .../tests/test_functional_create_service.py | 142 +++++++++--------- 10 files changed, 267 insertions(+), 235 deletions(-) diff --git a/src/tests/oeccpsc22/deploy_in_kubernetes.sh b/src/tests/oeccpsc22/deploy_in_kubernetes.sh index f40257f34..e350b8355 100755 --- a/src/tests/oeccpsc22/deploy_in_kubernetes.sh +++ b/src/tests/oeccpsc22/deploy_in_kubernetes.sh @@ -17,7 +17,7 @@ # OECC/PSC 22 deployment settings export REGISTRY_IMAGE="" -export COMPONENTS="context device service compute monitoring interdomain webui" # slice +export COMPONENTS="context device monitoring service slice interdomain compute" # webui export IMAGE_TAG="oeccpsc22" export K8S_HOSTNAME="kubernetes-master" #export GRAFANA_PASSWORD="admin123+" diff --git a/src/tests/oeccpsc22/run_test_01_bootstrap.sh b/src/tests/oeccpsc22/run_test_01_bootstrap.sh index 7b816984a..e8df6ffb6 100755 --- a/src/tests/oeccpsc22/run_test_01_bootstrap.sh +++ b/src/tests/oeccpsc22/run_test_01_bootstrap.sh @@ -41,11 +41,15 @@ export D1_CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonp export D1_CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') export D1_DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') export D1_DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export D1_COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') export D2_CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') export D2_CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') export D2_DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') export D2_DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export D2_COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') # Useful flags for pytest: #-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG diff --git a/src/tests/oeccpsc22/run_test_02_create_service.sh b/src/tests/oeccpsc22/run_test_02_create_service.sh index c01f64741..5101336ba 100755 --- a/src/tests/oeccpsc22/run_test_02_create_service.sh +++ b/src/tests/oeccpsc22/run_test_02_create_service.sh @@ -21,16 +21,24 @@ RCFILE=$PROJECTDIR/coverage/.coveragerc COVERAGEFILE=$PROJECTDIR/coverage/.coverage # Set the name of the Kubernetes namespace and hostname to use. -K8S_NAMESPACE="oeccpsc22" +K8S_NAMESPACE_D1="oeccpsc22-1" +K8S_NAMESPACE_D2="oeccpsc22-2" # dynamically gets the name of the K8s master node K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'` -export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') -export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') -export DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') -export DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') -export COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') -export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') +export D1_CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export D1_DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export D1_COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') + +export D2_CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export D2_DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export D2_COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') # Useful flags for pytest: #-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG diff --git a/src/tests/oeccpsc22/run_test_03_delete_service.sh b/src/tests/oeccpsc22/run_test_03_delete_service.sh index 1782a143b..e8549a370 100755 --- a/src/tests/oeccpsc22/run_test_03_delete_service.sh +++ b/src/tests/oeccpsc22/run_test_03_delete_service.sh @@ -21,16 +21,24 @@ RCFILE=$PROJECTDIR/coverage/.coveragerc COVERAGEFILE=$PROJECTDIR/coverage/.coverage # Set the name of the Kubernetes namespace and hostname to use. -K8S_NAMESPACE="oeccpsc22" +K8S_NAMESPACE_D1="oeccpsc22-1" +K8S_NAMESPACE_D2="oeccpsc22-2" # dynamically gets the name of the K8s master node K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'` -export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') -export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') -export DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') -export DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') -export COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') -export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') +export D1_CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export D1_DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export D1_COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') + +export D2_CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export D2_DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export D2_COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') # Useful flags for pytest: #-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG diff --git a/src/tests/oeccpsc22/run_test_04_cleanup.sh b/src/tests/oeccpsc22/run_test_04_cleanup.sh index 14b4024be..d0420820c 100755 --- a/src/tests/oeccpsc22/run_test_04_cleanup.sh +++ b/src/tests/oeccpsc22/run_test_04_cleanup.sh @@ -21,16 +21,24 @@ RCFILE=$PROJECTDIR/coverage/.coveragerc COVERAGEFILE=$PROJECTDIR/coverage/.coverage # Set the name of the Kubernetes namespace and hostname to use. -K8S_NAMESPACE="oeccpsc22" +K8S_NAMESPACE_D1="oeccpsc22-1" +K8S_NAMESPACE_D2="oeccpsc22-2" # dynamically gets the name of the K8s master node K8S_HOSTNAME=`kubectl get nodes --selector=node-role.kubernetes.io/master | tr -s " " | cut -f1 -d" " | sed -n '2 p'` -export CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') -export CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') -export DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') -export DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') -export COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') -export COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') +export D1_CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export D1_DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export D1_COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D1_COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE_D1 -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') + +export D2_CONTEXTSERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_CONTEXTSERVICE_SERVICE_PORT_GRPC=$(kubectl get service contextservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==1010)].nodePort}') +export D2_DEVICESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_DEVICESERVICE_SERVICE_PORT_GRPC=$(kubectl get service deviceservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==2020)].nodePort}') +export D2_COMPUTESERVICE_SERVICE_HOST=$(kubectl get node $K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +export D2_COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice-public --namespace $K8S_NAMESPACE_D2 -o 'jsonpath={.spec.ports[?(@.port==8080)].nodePort}') # Useful flags for pytest: #-o log_cli=true -o log_file=device.log -o log_file_level=DEBUG diff --git a/src/tests/oeccpsc22/tests/Objects_Domain_1.py b/src/tests/oeccpsc22/tests/Objects_Domain_1.py index af353c6e9..6fefc2d82 100644 --- a/src/tests/oeccpsc22/tests/Objects_Domain_1.py +++ b/src/tests/oeccpsc22/tests/Objects_Domain_1.py @@ -30,7 +30,7 @@ D1_TOPOLOGY = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=D1_CONTEXT_ID) # ----- Devices -------------------------------------------------------------------------------------------------------- # Assume all devices have the same architecture of endpoints -DEVICE_ENDPOINT_DEFS = [ +D1_DEVICE_ENDPOINT_DEFS = [ # Trunk ports ('1/1', '25Gbps', []), ('1/2', '25Gbps', []), ('1/3', '25Gbps', []), ('1/4', '25Gbps', []), # Inter-domain ports @@ -40,67 +40,72 @@ DEVICE_ENDPOINT_DEFS = [ ('3/5', '10Gbps', []), ('3/6', '10Gbps', []), ('3/7', '10Gbps', []), ('3/8', '10Gbps', []), ] -DEVICE_D1R1_UUID = 'D1-R1' -DEVICE_D1R1_ID = json_device_id(DEVICE_D1R1_UUID) -DEVICE_D1R1 = json_device_emulated_packet_router_disabled(DEVICE_D1R1_UUID) -DEVICE_D1R1_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) +D1_DEVICE_D1R1_UUID = 'D1-R1' +D1_DEVICE_D1R1_ID = json_device_id(D1_DEVICE_D1R1_UUID) +D1_DEVICE_D1R1 = json_device_emulated_packet_router_disabled(D1_DEVICE_D1R1_UUID) +D1_DEVICE_D1R1_CONNECT_RULES = json_device_emulated_connect_rules(D1_DEVICE_ENDPOINT_DEFS) -DEVICE_D1R2_UUID = 'D1-R2' -DEVICE_D1R2_ID = json_device_id(DEVICE_D1R2_UUID) -DEVICE_D1R2 = json_device_emulated_packet_router_disabled(DEVICE_D1R2_UUID) -DEVICE_D1R2_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) +D1_DEVICE_D1R2_UUID = 'D1-R2' +D1_DEVICE_D1R2_ID = json_device_id(D1_DEVICE_D1R2_UUID) +D1_DEVICE_D1R2 = json_device_emulated_packet_router_disabled(D1_DEVICE_D1R2_UUID) +D1_DEVICE_D1R2_CONNECT_RULES = json_device_emulated_connect_rules(D1_DEVICE_ENDPOINT_DEFS) -DEVICE_D1R3_UUID = 'D1-R3' -DEVICE_D1R3_ID = json_device_id(DEVICE_D1R3_UUID) -DEVICE_D1R3 = json_device_emulated_packet_router_disabled(DEVICE_D1R3_UUID) -DEVICE_D1R3_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) +D1_DEVICE_D1R3_UUID = 'D1-R3' +D1_DEVICE_D1R3_ID = json_device_id(D1_DEVICE_D1R3_UUID) +D1_DEVICE_D1R3 = json_device_emulated_packet_router_disabled(D1_DEVICE_D1R3_UUID) +D1_DEVICE_D1R3_CONNECT_RULES = json_device_emulated_connect_rules(D1_DEVICE_ENDPOINT_DEFS) -DEVICE_D1R4_UUID = 'D1-R4' -DEVICE_D1R4_ID = json_device_id(DEVICE_D1R4_UUID) -DEVICE_D1R4 = json_device_emulated_packet_router_disabled(DEVICE_D1R4_UUID) -DEVICE_D1R4_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) +D1_DEVICE_D1R4_UUID = 'D1-R4' +D1_DEVICE_D1R4_ID = json_device_id(D1_DEVICE_D1R4_UUID) +D1_DEVICE_D1R4 = json_device_emulated_packet_router_disabled(D1_DEVICE_D1R4_UUID) +D1_DEVICE_D1R4_CONNECT_RULES = json_device_emulated_connect_rules(D1_DEVICE_ENDPOINT_DEFS) # Virtual devices on remote domains -DEVICE_D2R1_UUID = 'D2-R1' -DEVICE_D2R1_ID = json_device_id(DEVICE_D2R1_UUID) -DEVICE_D2R1 = json_device_emulated_packet_router_disabled(DEVICE_D2R1_UUID) -DEVICE_D2R1_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) +D1_DEVICE_D2R1_UUID = 'D2-R1' +D1_DEVICE_D2R1_ID = json_device_id(D1_DEVICE_D2R1_UUID) +D1_DEVICE_D2R1 = json_device_emulated_packet_router_disabled(D1_DEVICE_D2R1_UUID) +D1_DEVICE_D2R1_CONNECT_RULES = json_device_emulated_connect_rules(D1_DEVICE_ENDPOINT_DEFS) -ENDPOINT_IDS = {} -ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R1_ID, DEVICE_ENDPOINT_DEFS)) -ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R2_ID, DEVICE_ENDPOINT_DEFS)) -ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R3_ID, DEVICE_ENDPOINT_DEFS)) -ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R4_ID, DEVICE_ENDPOINT_DEFS)) -ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R1_ID, DEVICE_ENDPOINT_DEFS)) +D1_ENDPOINT_IDS = {} +D1_ENDPOINT_IDS.update(json_endpoint_ids(D1_DEVICE_D1R1_ID, D1_DEVICE_ENDPOINT_DEFS)) +D1_ENDPOINT_IDS.update(json_endpoint_ids(D1_DEVICE_D1R2_ID, D1_DEVICE_ENDPOINT_DEFS)) +D1_ENDPOINT_IDS.update(json_endpoint_ids(D1_DEVICE_D1R3_ID, D1_DEVICE_ENDPOINT_DEFS)) +D1_ENDPOINT_IDS.update(json_endpoint_ids(D1_DEVICE_D1R4_ID, D1_DEVICE_ENDPOINT_DEFS)) +D1_ENDPOINT_IDS.update(json_endpoint_ids(D1_DEVICE_D2R1_ID, D1_DEVICE_ENDPOINT_DEFS)) # ----- Links ---------------------------------------------------------------------------------------------------------- # Intra-domain links -LINK_D1R1_D1R2_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R1_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R2_UUID]['1/1']) -LINK_D1R1_D1R2_ID = json_link_id(LINK_D1R1_D1R2_UUID) -LINK_D1R1_D1R2 = json_link(LINK_D1R1_D1R2_UUID, [ - ENDPOINT_IDS[DEVICE_D1R1_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R2_UUID]['1/1']]) - -LINK_D1R2_D1R3_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R2_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R3_UUID]['1/1']) -LINK_D1R2_D1R3_ID = json_link_id(LINK_D1R2_D1R3_UUID) -LINK_D1R2_D1R3 = json_link(LINK_D1R2_D1R3_UUID, [ - ENDPOINT_IDS[DEVICE_D1R2_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R3_UUID]['1/1']]) - -LINK_D1R3_D1R4_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R3_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R4_UUID]['1/1']) -LINK_D1R3_D1R4_ID = json_link_id(LINK_D1R3_D1R4_UUID) -LINK_D1R3_D1R4 = json_link(LINK_D1R3_D1R4_UUID, [ - ENDPOINT_IDS[DEVICE_D1R3_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R4_UUID]['1/1']]) - -LINK_D1R4_D1R1_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R4_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R1_UUID]['1/1']) -LINK_D1R4_D1R1_ID = json_link_id(LINK_D1R4_D1R1_UUID) -LINK_D1R4_D1R1 = json_link(LINK_D1R4_D1R1_UUID, [ - ENDPOINT_IDS[DEVICE_D1R4_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D1R1_UUID]['1/1']]) +D1_LINK_D1R1_D1R2_UUID = get_link_uuid( + D1_ENDPOINT_IDS[D1_DEVICE_D1R1_UUID]['1/2'], D1_ENDPOINT_IDS[D1_DEVICE_D1R2_UUID]['1/1']) +D1_LINK_D1R1_D1R2_ID = json_link_id(D1_LINK_D1R1_D1R2_UUID) +D1_LINK_D1R1_D1R2 = json_link(D1_LINK_D1R1_D1R2_UUID, [ + D1_ENDPOINT_IDS[D1_DEVICE_D1R1_UUID]['1/2'], D1_ENDPOINT_IDS[D1_DEVICE_D1R2_UUID]['1/1']]) + +D1_LINK_D1R2_D1R3_UUID = get_link_uuid( + D1_ENDPOINT_IDS[D1_DEVICE_D1R2_UUID]['1/2'], D1_ENDPOINT_IDS[D1_DEVICE_D1R3_UUID]['1/1']) +D1_LINK_D1R2_D1R3_ID = json_link_id(D1_LINK_D1R2_D1R3_UUID) +D1_LINK_D1R2_D1R3 = json_link(D1_LINK_D1R2_D1R3_UUID, [ + D1_ENDPOINT_IDS[D1_DEVICE_D1R2_UUID]['1/2'], D1_ENDPOINT_IDS[D1_DEVICE_D1R3_UUID]['1/1']]) + +D1_LINK_D1R3_D1R4_UUID = get_link_uuid( + D1_ENDPOINT_IDS[D1_DEVICE_D1R3_UUID]['1/2'], D1_ENDPOINT_IDS[D1_DEVICE_D1R4_UUID]['1/1']) +D1_LINK_D1R3_D1R4_ID = json_link_id(D1_LINK_D1R3_D1R4_UUID) +D1_LINK_D1R3_D1R4 = json_link(D1_LINK_D1R3_D1R4_UUID, [ + D1_ENDPOINT_IDS[D1_DEVICE_D1R3_UUID]['1/2'], D1_ENDPOINT_IDS[D1_DEVICE_D1R4_UUID]['1/1']]) + +D1_LINK_D1R4_D1R1_UUID = get_link_uuid( + D1_ENDPOINT_IDS[D1_DEVICE_D1R4_UUID]['1/2'], D1_ENDPOINT_IDS[D1_DEVICE_D1R1_UUID]['1/1']) +D1_LINK_D1R4_D1R1_ID = json_link_id(D1_LINK_D1R4_D1R1_UUID) +D1_LINK_D1R4_D1R1 = json_link(D1_LINK_D1R4_D1R1_UUID, [ + D1_ENDPOINT_IDS[D1_DEVICE_D1R4_UUID]['1/2'], D1_ENDPOINT_IDS[D1_DEVICE_D1R1_UUID]['1/1']]) # Inter-domain links -LINK_D1R4_D2R1_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D1R4_UUID]['2/1'], ENDPOINT_IDS[DEVICE_D2R1_UUID]['2/1']) -LINK_D1R4_D2R1_ID = json_link_id(LINK_D1R4_D2R1_UUID) -LINK_D1R4_D2R1 = json_link(LINK_D1R4_D2R1_UUID, [ - ENDPOINT_IDS[DEVICE_D1R4_UUID]['2/1'], ENDPOINT_IDS[DEVICE_D2R1_UUID]['2/1']]) +D1_LINK_D1R4_D2R1_UUID = get_link_uuid( + D1_ENDPOINT_IDS[D1_DEVICE_D1R4_UUID]['2/1'], D1_ENDPOINT_IDS[D1_DEVICE_D2R1_UUID]['2/1']) +D1_LINK_D1R4_D2R1_ID = json_link_id(D1_LINK_D1R4_D2R1_UUID) +D1_LINK_D1R4_D2R1 = json_link(D1_LINK_D1R4_D2R1_UUID, [ + D1_ENDPOINT_IDS[D1_DEVICE_D1R4_UUID]['2/1'], D1_ENDPOINT_IDS[D1_DEVICE_D2R1_UUID]['2/1']]) # ----- Object Collections --------------------------------------------------------------------------------------------- @@ -108,14 +113,14 @@ D1_CONTEXTS = [D1_CONTEXT] D1_TOPOLOGIES = [D1_TOPOLOGY] D1_DEVICES = [ - (DEVICE_D1R1, DEVICE_D1R1_CONNECT_RULES), - (DEVICE_D1R2, DEVICE_D1R2_CONNECT_RULES), - (DEVICE_D1R3, DEVICE_D1R3_CONNECT_RULES), - (DEVICE_D1R4, DEVICE_D1R4_CONNECT_RULES), - (DEVICE_D2R1, DEVICE_D2R1_CONNECT_RULES), + (D1_DEVICE_D1R1, D1_DEVICE_D1R1_CONNECT_RULES), + (D1_DEVICE_D1R2, D1_DEVICE_D1R2_CONNECT_RULES), + (D1_DEVICE_D1R3, D1_DEVICE_D1R3_CONNECT_RULES), + (D1_DEVICE_D1R4, D1_DEVICE_D1R4_CONNECT_RULES), + (D1_DEVICE_D2R1, D1_DEVICE_D2R1_CONNECT_RULES), ] D1_LINKS = [ - LINK_D1R1_D1R2, LINK_D1R2_D1R3, LINK_D1R3_D1R4, LINK_D1R4_D1R1, - LINK_D1R4_D2R1, + D1_LINK_D1R1_D1R2, D1_LINK_D1R2_D1R3, D1_LINK_D1R3_D1R4, D1_LINK_D1R4_D1R1, + D1_LINK_D1R4_D2R1, ] diff --git a/src/tests/oeccpsc22/tests/Objects_Domain_2.py b/src/tests/oeccpsc22/tests/Objects_Domain_2.py index f77989250..ec641dce4 100644 --- a/src/tests/oeccpsc22/tests/Objects_Domain_2.py +++ b/src/tests/oeccpsc22/tests/Objects_Domain_2.py @@ -30,7 +30,7 @@ D2_TOPOLOGY = json_topology(DEFAULT_TOPOLOGY_UUID, context_id=D2_CONTEXT_ID) # ----- Devices -------------------------------------------------------------------------------------------------------- # Assume all devices have the same architecture of endpoints -DEVICE_ENDPOINT_DEFS = [ +D2_DEVICE_ENDPOINT_DEFS = [ # Trunk ports ('1/1', '25Gbps', []), ('1/2', '25Gbps', []), ('1/3', '25Gbps', []), ('1/4', '25Gbps', []), # Inter-domain ports @@ -40,67 +40,72 @@ DEVICE_ENDPOINT_DEFS = [ ('3/5', '10Gbps', []), ('3/6', '10Gbps', []), ('3/7', '10Gbps', []), ('3/8', '10Gbps', []), ] -DEVICE_D2R1_UUID = 'D2-R1' -DEVICE_D2R1_ID = json_device_id(DEVICE_D2R1_UUID) -DEVICE_D2R1 = json_device_emulated_packet_router_disabled(DEVICE_D2R1_UUID) -DEVICE_D2R1_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) +D2_DEVICE_D2R1_UUID = 'D2-R1' +D2_DEVICE_D2R1_ID = json_device_id(D2_DEVICE_D2R1_UUID) +D2_DEVICE_D2R1 = json_device_emulated_packet_router_disabled(D2_DEVICE_D2R1_UUID) +D2_DEVICE_D2R1_CONNECT_RULES = json_device_emulated_connect_rules(D2_DEVICE_ENDPOINT_DEFS) -DEVICE_D2R2_UUID = 'D2-R2' -DEVICE_D2R2_ID = json_device_id(DEVICE_D2R2_UUID) -DEVICE_D2R2 = json_device_emulated_packet_router_disabled(DEVICE_D2R2_UUID) -DEVICE_D2R2_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) +D2_DEVICE_D2R2_UUID = 'D2-R2' +D2_DEVICE_D2R2_ID = json_device_id(D2_DEVICE_D2R2_UUID) +D2_DEVICE_D2R2 = json_device_emulated_packet_router_disabled(D2_DEVICE_D2R2_UUID) +D2_DEVICE_D2R2_CONNECT_RULES = json_device_emulated_connect_rules(D2_DEVICE_ENDPOINT_DEFS) -DEVICE_D2R3_UUID = 'D2-R3' -DEVICE_D2R3_ID = json_device_id(DEVICE_D2R3_UUID) -DEVICE_D2R3 = json_device_emulated_packet_router_disabled(DEVICE_D2R3_UUID) -DEVICE_D2R3_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) +D2_DEVICE_D2R3_UUID = 'D2-R3' +D2_DEVICE_D2R3_ID = json_device_id(D2_DEVICE_D2R3_UUID) +D2_DEVICE_D2R3 = json_device_emulated_packet_router_disabled(D2_DEVICE_D2R3_UUID) +D2_DEVICE_D2R3_CONNECT_RULES = json_device_emulated_connect_rules(D2_DEVICE_ENDPOINT_DEFS) -DEVICE_D2R4_UUID = 'D2-R4' -DEVICE_D2R4_ID = json_device_id(DEVICE_D2R4_UUID) -DEVICE_D2R4 = json_device_emulated_packet_router_disabled(DEVICE_D2R4_UUID) -DEVICE_D2R4_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) +D2_DEVICE_D2R4_UUID = 'D2-R4' +D2_DEVICE_D2R4_ID = json_device_id(D2_DEVICE_D2R4_UUID) +D2_DEVICE_D2R4 = json_device_emulated_packet_router_disabled(D2_DEVICE_D2R4_UUID) +D2_DEVICE_D2R4_CONNECT_RULES = json_device_emulated_connect_rules(D2_DEVICE_ENDPOINT_DEFS) # Virtual devices on remote domains -DEVICE_D1R1_UUID = 'D1-R1' -DEVICE_D1R1_ID = json_device_id(DEVICE_D1R1_UUID) -DEVICE_D1R1 = json_device_emulated_packet_router_disabled(DEVICE_D1R1_UUID) -DEVICE_D1R1_CONNECT_RULES = json_device_emulated_connect_rules(DEVICE_ENDPOINT_DEFS) +D2_DEVICE_D1R1_UUID = 'D1-R1' +D2_DEVICE_D1R1_ID = json_device_id(D2_DEVICE_D1R1_UUID) +D2_DEVICE_D1R1 = json_device_emulated_packet_router_disabled(D2_DEVICE_D1R1_UUID) +D2_DEVICE_D1R1_CONNECT_RULES = json_device_emulated_connect_rules(D2_DEVICE_ENDPOINT_DEFS) -ENDPOINT_IDS = {} -ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R1_ID, DEVICE_ENDPOINT_DEFS)) -ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R2_ID, DEVICE_ENDPOINT_DEFS)) -ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R3_ID, DEVICE_ENDPOINT_DEFS)) -ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D2R4_ID, DEVICE_ENDPOINT_DEFS)) -ENDPOINT_IDS.update(json_endpoint_ids(DEVICE_D1R1_ID, DEVICE_ENDPOINT_DEFS)) +D2_ENDPOINT_IDS = {} +D2_ENDPOINT_IDS.update(json_endpoint_ids(D2_DEVICE_D2R1_ID, D2_DEVICE_ENDPOINT_DEFS)) +D2_ENDPOINT_IDS.update(json_endpoint_ids(D2_DEVICE_D2R2_ID, D2_DEVICE_ENDPOINT_DEFS)) +D2_ENDPOINT_IDS.update(json_endpoint_ids(D2_DEVICE_D2R3_ID, D2_DEVICE_ENDPOINT_DEFS)) +D2_ENDPOINT_IDS.update(json_endpoint_ids(D2_DEVICE_D2R4_ID, D2_DEVICE_ENDPOINT_DEFS)) +D2_ENDPOINT_IDS.update(json_endpoint_ids(D2_DEVICE_D1R1_ID, D2_DEVICE_ENDPOINT_DEFS)) # ----- Links ---------------------------------------------------------------------------------------------------------- # Intra-domain links -LINK_D2R1_D2R2_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R1_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R2_UUID]['1/1']) -LINK_D2R1_D2R2_ID = json_link_id(LINK_D2R1_D2R2_UUID) -LINK_D2R1_D2R2 = json_link(LINK_D2R1_D2R2_UUID, [ - ENDPOINT_IDS[DEVICE_D2R1_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R2_UUID]['1/1']]) - -LINK_D2R2_D2R3_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R2_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R3_UUID]['1/1']) -LINK_D2R2_D2R3_ID = json_link_id(LINK_D2R2_D2R3_UUID) -LINK_D2R2_D2R3 = json_link(LINK_D2R2_D2R3_UUID, [ - ENDPOINT_IDS[DEVICE_D2R2_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R3_UUID]['1/1']]) - -LINK_D2R3_D2R4_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R3_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R4_UUID]['1/1']) -LINK_D2R3_D2R4_ID = json_link_id(LINK_D2R3_D2R4_UUID) -LINK_D2R3_D2R4 = json_link(LINK_D2R3_D2R4_UUID, [ - ENDPOINT_IDS[DEVICE_D2R3_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R4_UUID]['1/1']]) - -LINK_D2R4_D2R1_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R4_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R1_UUID]['1/1']) -LINK_D2R4_D2R1_ID = json_link_id(LINK_D2R4_D2R1_UUID) -LINK_D2R4_D2R1 = json_link(LINK_D2R4_D2R1_UUID, [ - ENDPOINT_IDS[DEVICE_D2R4_UUID]['1/2'], ENDPOINT_IDS[DEVICE_D2R1_UUID]['1/1']]) +D2_LINK_D2R1_D2R2_UUID = get_link_uuid( + D2_ENDPOINT_IDS[D2_DEVICE_D2R1_UUID]['1/2'], D2_ENDPOINT_IDS[D2_DEVICE_D2R2_UUID]['1/1']) +D2_LINK_D2R1_D2R2_ID = json_link_id(D2_LINK_D2R1_D2R2_UUID) +D2_LINK_D2R1_D2R2 = json_link(D2_LINK_D2R1_D2R2_UUID, [ + D2_ENDPOINT_IDS[D2_DEVICE_D2R1_UUID]['1/2'], D2_ENDPOINT_IDS[D2_DEVICE_D2R2_UUID]['1/1']]) + +D2_LINK_D2R2_D2R3_UUID = get_link_uuid( + D2_ENDPOINT_IDS[D2_DEVICE_D2R2_UUID]['1/2'], D2_ENDPOINT_IDS[D2_DEVICE_D2R3_UUID]['1/1']) +D2_LINK_D2R2_D2R3_ID = json_link_id(D2_LINK_D2R2_D2R3_UUID) +D2_LINK_D2R2_D2R3 = json_link(D2_LINK_D2R2_D2R3_UUID, [ + D2_ENDPOINT_IDS[D2_DEVICE_D2R2_UUID]['1/2'], D2_ENDPOINT_IDS[D2_DEVICE_D2R3_UUID]['1/1']]) + +D2_LINK_D2R3_D2R4_UUID = get_link_uuid( + D2_ENDPOINT_IDS[D2_DEVICE_D2R3_UUID]['1/2'], D2_ENDPOINT_IDS[D2_DEVICE_D2R4_UUID]['1/1']) +D2_LINK_D2R3_D2R4_ID = json_link_id(D2_LINK_D2R3_D2R4_UUID) +D2_LINK_D2R3_D2R4 = json_link(D2_LINK_D2R3_D2R4_UUID, [ + D2_ENDPOINT_IDS[D2_DEVICE_D2R3_UUID]['1/2'], D2_ENDPOINT_IDS[D2_DEVICE_D2R4_UUID]['1/1']]) + +D2_LINK_D2R4_D2R1_UUID = get_link_uuid( + D2_ENDPOINT_IDS[D2_DEVICE_D2R4_UUID]['1/2'], D2_ENDPOINT_IDS[D2_DEVICE_D2R1_UUID]['1/1']) +D2_LINK_D2R4_D2R1_ID = json_link_id(D2_LINK_D2R4_D2R1_UUID) +D2_LINK_D2R4_D2R1 = json_link(D2_LINK_D2R4_D2R1_UUID, [ + D2_ENDPOINT_IDS[D2_DEVICE_D2R4_UUID]['1/2'], D2_ENDPOINT_IDS[D2_DEVICE_D2R1_UUID]['1/1']]) # Inter-domain links -LINK_D2R4_D1R1_UUID = get_link_uuid(ENDPOINT_IDS[DEVICE_D2R4_UUID]['2/1'], ENDPOINT_IDS[DEVICE_D1R1_UUID]['2/1']) -LINK_D2R4_D1R1_ID = json_link_id(LINK_D2R4_D1R1_UUID) -LINK_D2R4_D1R1 = json_link(LINK_D2R4_D1R1_UUID, [ - ENDPOINT_IDS[DEVICE_D2R4_UUID]['2/1'], ENDPOINT_IDS[DEVICE_D1R1_UUID]['2/1']]) +D2_LINK_D2R4_D1R1_UUID = get_link_uuid( + D2_ENDPOINT_IDS[D2_DEVICE_D2R4_UUID]['2/1'], D2_ENDPOINT_IDS[D2_DEVICE_D1R1_UUID]['2/1']) +D2_LINK_D2R4_D1R1_ID = json_link_id(D2_LINK_D2R4_D1R1_UUID) +D2_LINK_D2R4_D1R1 = json_link(D2_LINK_D2R4_D1R1_UUID, [ + D2_ENDPOINT_IDS[D2_DEVICE_D2R4_UUID]['2/1'], D2_ENDPOINT_IDS[D2_DEVICE_D1R1_UUID]['2/1']]) # ----- Object Collections --------------------------------------------------------------------------------------------- @@ -108,14 +113,14 @@ D2_CONTEXTS = [D2_CONTEXT] D2_TOPOLOGIES = [D2_TOPOLOGY] D2_DEVICES = [ - (DEVICE_D2R1, DEVICE_D2R1_CONNECT_RULES), - (DEVICE_D2R2, DEVICE_D2R2_CONNECT_RULES), - (DEVICE_D2R3, DEVICE_D2R3_CONNECT_RULES), - (DEVICE_D2R4, DEVICE_D2R4_CONNECT_RULES), - (DEVICE_D1R1, DEVICE_D1R1_CONNECT_RULES), + (D2_DEVICE_D2R1, D2_DEVICE_D2R1_CONNECT_RULES), + (D2_DEVICE_D2R2, D2_DEVICE_D2R2_CONNECT_RULES), + (D2_DEVICE_D2R3, D2_DEVICE_D2R3_CONNECT_RULES), + (D2_DEVICE_D2R4, D2_DEVICE_D2R4_CONNECT_RULES), + (D2_DEVICE_D1R1, D2_DEVICE_D1R1_CONNECT_RULES), ] D2_LINKS = [ - LINK_D2R1_D2R2, LINK_D2R2_D2R3, LINK_D2R3_D2R4, LINK_D2R4_D2R1, - LINK_D2R4_D1R1, + D2_LINK_D2R1_D2R2, D2_LINK_D2R2_D2R3, D2_LINK_D2R3_D2R4, D2_LINK_D2R4_D2R1, + D2_LINK_D2R4_D1R1, ] diff --git a/src/tests/oeccpsc22/tests/Objects_Service.py b/src/tests/oeccpsc22/tests/Objects_Service.py index b9ec2a691..a9ffadc0f 100644 --- a/src/tests/oeccpsc22/tests/Objects_Service.py +++ b/src/tests/oeccpsc22/tests/Objects_Service.py @@ -1,35 +1,37 @@ - +from .Objects_Domain_1 import D1_DEVICE_D1R1_UUID, D1_ENDPOINT_IDS +from .Objects_Domain_2 import D2_DEVICE_D2R4_UUID, D2_ENDPOINT_IDS +from .Tools import compose_bearer, compose_service_endpoint_id # ----- WIM Service Settings ------------------------------------------------------------------------------------------- -WIM_SEP_R1_ID = compose_service_endpoint_id(ENDPOINT_ID_R1_13_1_2) -WIM_SEP_R1_ROUTER_ID = '10.10.10.1' -WIM_SEP_R1_ROUTER_DIST = '65000:111' -WIM_SEP_R1_SITE_ID = '1' -WIM_SEP_R1_BEARER = compose_bearer(ENDPOINT_ID_R1_13_1_2, WIM_SEP_R1_ROUTER_ID, WIM_SEP_R1_ROUTER_DIST) -WIM_SRV_R1_VLAN_ID = 400 +WIM_SEP_D1R1_ID = compose_service_endpoint_id(D1_ENDPOINT_IDS[D1_DEVICE_D1R1_UUID]['3/1']) +WIM_SEP_D1R1_ROUTER_ID = '10.10.10.1' +WIM_SEP_D1R1_ROUTER_DIST = '65000:111' +WIM_SEP_D1R1_SITE_ID = '1' +WIM_SEP_D1R1_BEARER = compose_bearer(D1_ENDPOINT_IDS[D1_DEVICE_D1R1_UUID]['3/1']) +WIM_SRV_D1R1_VLAN_ID = 400 -WIM_SEP_R3_ID = compose_service_endpoint_id(ENDPOINT_ID_R3_13_1_2) -WIM_SEP_R3_ROUTER_ID = '20.20.20.1' -WIM_SEP_R3_ROUTER_DIST = '65000:222' -WIM_SEP_R3_SITE_ID = '2' -WIM_SEP_R3_BEARER = compose_bearer(ENDPOINT_ID_R3_13_1_2, WIM_SEP_R3_ROUTER_ID, WIM_SEP_R3_ROUTER_DIST) -WIM_SRV_R3_VLAN_ID = 500 +WIM_SEP_D2R4_ID = compose_service_endpoint_id(D2_ENDPOINT_IDS[D2_DEVICE_D2R4_UUID]['3/3']) +WIM_SEP_D2R4_ROUTER_ID = '20.20.20.1' +WIM_SEP_D2R4_ROUTER_DIST = '65000:222' +WIM_SEP_D2R4_SITE_ID = '2' +WIM_SEP_D2R4_BEARER = compose_bearer(D2_ENDPOINT_IDS[D2_DEVICE_D2R4_UUID]['3/3']) +WIM_SRV_D2R4_VLAN_ID = 500 WIM_USERNAME = 'admin' WIM_PASSWORD = 'admin' WIM_MAPPING = [ - {'device-id': DEVICE_R1_UUID, 'service_endpoint_id': WIM_SEP_R1_ID, - 'service_mapping_info': {'bearer': {'bearer-reference': WIM_SEP_R1_BEARER}, 'site-id': WIM_SEP_R1_SITE_ID}}, - {'device-id': DEVICE_R3_UUID, 'service_endpoint_id': WIM_SEP_R3_ID, - 'service_mapping_info': {'bearer': {'bearer-reference': WIM_SEP_R3_BEARER}, 'site-id': WIM_SEP_R3_SITE_ID}}, + {'device-id': D1_DEVICE_D1R1_UUID, 'service_endpoint_id': WIM_SEP_D1R1_ID, + 'service_mapping_info': {'bearer': {'bearer-reference': WIM_SEP_D1R1_BEARER}, 'site-id': WIM_SEP_D1R1_SITE_ID}}, + {'device-id': D2_DEVICE_D2R4_UUID, 'service_endpoint_id': WIM_SEP_D2R4_ID, + 'service_mapping_info': {'bearer': {'bearer-reference': WIM_SEP_D2R4_BEARER}, 'site-id': WIM_SEP_D2R4_SITE_ID}}, ] -WIM_SERVICE_TYPE = 'ELINE' +WIM_SERVICE_TYPE = 'ELAN' WIM_SERVICE_CONNECTION_POINTS = [ - {'service_endpoint_id': WIM_SEP_R1_ID, + {'service_endpoint_id': WIM_SEP_D1R1_ID, 'service_endpoint_encapsulation_type': 'dot1q', - 'service_endpoint_encapsulation_info': {'vlan': WIM_SRV_R1_VLAN_ID}}, - {'service_endpoint_id': WIM_SEP_R3_ID, + 'service_endpoint_encapsulation_info': {'vlan': WIM_SRV_D1R1_VLAN_ID}}, + {'service_endpoint_id': WIM_SEP_D2R4_ID, 'service_endpoint_encapsulation_type': 'dot1q', - 'service_endpoint_encapsulation_info': {'vlan': WIM_SRV_R3_VLAN_ID}}, + 'service_endpoint_encapsulation_info': {'vlan': WIM_SRV_D2R4_VLAN_ID}}, ] diff --git a/src/tests/oeccpsc22/tests/test_functional_bootstrap.py b/src/tests/oeccpsc22/tests/test_functional_bootstrap.py index b09b558cd..413baa57e 100644 --- a/src/tests/oeccpsc22/tests/test_functional_bootstrap.py +++ b/src/tests/oeccpsc22/tests/test_functional_bootstrap.py @@ -14,11 +14,7 @@ import copy, logging, pytest from common.Settings import get_setting -from common.tests.EventTools import EVENT_CREATE, check_events -from common.tools.object_factory.Device import json_device_id -from common.tools.object_factory.Link import json_link_id from context.client.ContextClient import ContextClient -from context.client.EventsCollector import EventsCollector from context.proto.context_pb2 import Context, ContextId, Device, Empty, Link, Topology from device.client.DeviceClient import DeviceClient from .Objects_Domain_1 import D1_CONTEXT_ID, D1_CONTEXTS, D1_DEVICES, D1_LINKS, D1_TOPOLOGIES diff --git a/src/tests/oeccpsc22/tests/test_functional_create_service.py b/src/tests/oeccpsc22/tests/test_functional_create_service.py index f3389fdbf..ea9ffad70 100644 --- a/src/tests/oeccpsc22/tests/test_functional_create_service.py +++ b/src/tests/oeccpsc22/tests/test_functional_create_service.py @@ -15,18 +15,19 @@ import logging, pytest from common.DeviceTypes import DeviceTypeEnum from common.Settings import get_setting -from common.tests.EventTools import EVENT_CREATE, EVENT_UPDATE, check_events -from common.tools.object_factory.Connection import json_connection_id -from common.tools.object_factory.Device import json_device_id -from common.tools.object_factory.Service import json_service_id from common.tools.grpc.Tools import grpc_message_to_json_string from compute.tests.mock_osm.MockOSM import MockOSM from context.client.ContextClient import ContextClient -from context.client.EventsCollector import EventsCollector from context.proto.context_pb2 import ContextId, Empty -from .Objects import ( - CONTEXT_ID, CONTEXTS, DEVICE_O1_UUID, DEVICE_R1_UUID, DEVICE_R3_UUID, DEVICES, LINKS, TOPOLOGIES, - WIM_MAPPING, WIM_PASSWORD, WIM_SERVICE_CONNECTION_POINTS, WIM_SERVICE_TYPE, WIM_USERNAME) +from tests.oeccpsc22.tests.Objects_Domain_1 import D1_CONTEXT_ID, D1_CONTEXTS, D1_DEVICES, D1_LINKS, D1_TOPOLOGIES +from tests.oeccpsc22.tests.Objects_Domain_2 import D2_CONTEXT_ID, D2_CONTEXTS, D2_DEVICES, D2_LINKS, D2_TOPOLOGIES +#from .Objects_Domain_1 import ( +# CONTEXT_ID, CONTEXTS, DEVICE_O1_UUID, DEVICE_R1_UUID, DEVICE_R3_UUID, DEVICES, LINKS, TOPOLOGIES, +# WIM_MAPPING, WIM_PASSWORD, WIM_SERVICE_CONNECTION_POINTS, WIM_SERVICE_TYPE, WIM_USERNAME) +#from .Objects_Domain_2 import ( +# CONTEXT_ID, CONTEXTS, DEVICE_O1_UUID, DEVICE_R1_UUID, DEVICE_R3_UUID, DEVICES, LINKS, TOPOLOGIES, +# WIM_MAPPING, WIM_PASSWORD, WIM_SERVICE_CONNECTION_POINTS, WIM_SERVICE_TYPE, WIM_USERNAME) +from .Objects_Service import WIM_MAPPING, WIM_PASSWORD, WIM_SERVICE_CONNECTION_POINTS, WIM_SERVICE_TYPE, WIM_USERNAME LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) @@ -36,94 +37,89 @@ DEVTYPE_EMU_OLS = DeviceTypeEnum.EMULATED_OPTICAL_LINE_SYSTEM.value @pytest.fixture(scope='session') -def context_client(): - _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) +def d1_context_client(): + _client = ContextClient( + get_setting('D1_CONTEXTSERVICE_SERVICE_HOST'), get_setting('D1_CONTEXTSERVICE_SERVICE_PORT_GRPC')) yield _client _client.close() @pytest.fixture(scope='session') -def osm_wim(): +def d2_context_client(): + _client = ContextClient( + get_setting('D2_CONTEXTSERVICE_SERVICE_HOST'), get_setting('D2_CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +@pytest.fixture(scope='session') +def d1_osm_wim(): wim_url = 'http://{:s}:{:s}'.format( - get_setting('COMPUTESERVICE_SERVICE_HOST'), str(get_setting('COMPUTESERVICE_SERVICE_PORT_HTTP'))) + get_setting('D1_COMPUTESERVICE_SERVICE_HOST'), str(get_setting('D1_COMPUTESERVICE_SERVICE_PORT_HTTP'))) return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD) -def test_scenario_is_correct(context_client : ContextClient): # pylint: disable=redefined-outer-name - # ----- List entities - Ensure links are created ------------------------------------------------------------------- - response = context_client.ListContexts(Empty()) - assert len(response.contexts) == len(CONTEXTS) +def test_scenario_is_correct( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(contexts, topologies, devices, links, context_id, context_client): + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(contexts) + + response = context_client.ListTopologies(ContextId(**context_id)) + assert len(response.topologies) == len(topologies) - response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) - assert len(response.topologies) == len(TOPOLOGIES) + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(devices) - response = context_client.ListDevices(Empty()) - assert len(response.devices) == len(DEVICES) + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(links) - response = context_client.ListLinks(Empty()) - assert len(response.links) == len(LINKS) + response = context_client.ListServices(ContextId(**context_id)) + assert len(response.services) == 0 - response = context_client.ListServices(ContextId(**CONTEXT_ID)) - assert len(response.services) == 0 + # ----- List entities - Ensure scenario is up ---------------------------------------------------------------------- + per_domain(D1_CONTEXTS, D1_TOPOLOGIES, D1_DEVICES, D1_LINKS, D1_CONTEXT_ID, d1_context_client) + per_domain(D2_CONTEXTS, D2_TOPOLOGIES, D2_DEVICES, D2_LINKS, D2_CONTEXT_ID, d2_context_client) -def test_service_creation(context_client : ContextClient, osm_wim : MockOSM): # pylint: disable=redefined-outer-name - # ----- Start the EventsCollector ---------------------------------------------------------------------------------- - events_collector = EventsCollector(context_client, log_events_received=True) - events_collector.start() +def test_service_creation( + d1_osm_wim : MockOSM): # pylint: disable=redefined-outer-name # ----- Create Service --------------------------------------------------------------------------------------------- - service_uuid = osm_wim.create_connectivity_service(WIM_SERVICE_TYPE, WIM_SERVICE_CONNECTION_POINTS) - osm_wim.get_connectivity_service_status(service_uuid) + service_uuid = d1_osm_wim.create_connectivity_service(WIM_SERVICE_TYPE, WIM_SERVICE_CONNECTION_POINTS) + d1_osm_wim.get_connectivity_service_status(service_uuid) - # ----- Validate collected events ---------------------------------------------------------------------------------- - packet_connection_uuid = '{:s}:{:s}'.format(service_uuid, DEVTYPE_EMU_PR) - optical_connection_uuid = '{:s}:optical:{:s}'.format(service_uuid, DEVTYPE_EMU_OLS) - optical_service_uuid = '{:s}:optical'.format(service_uuid) +def test_scenario_service_created( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name - expected_events = [ - # Create packet service and add first endpoint - ('ServiceEvent', EVENT_CREATE, json_service_id(service_uuid, context_id=CONTEXT_ID)), - ('ServiceEvent', EVENT_UPDATE, json_service_id(service_uuid, context_id=CONTEXT_ID)), + def per_domain(contexts, topologies, devices, links, context_id, context_client): + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(contexts) - # Configure OLS controller, create optical service, create optical connection - ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_O1_UUID)), - ('ServiceEvent', EVENT_CREATE, json_service_id(optical_service_uuid, context_id=CONTEXT_ID)), - ('ConnectionEvent', EVENT_CREATE, json_connection_id(optical_connection_uuid)), + response = context_client.ListTopologies(ContextId(**context_id)) + assert len(response.topologies) == len(topologies) - # Configure endpoint packet devices, add second endpoint to service, create connection - ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_R1_UUID)), - ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_R3_UUID)), - ('ServiceEvent', EVENT_UPDATE, json_service_id(service_uuid, context_id=CONTEXT_ID)), - ('ConnectionEvent', EVENT_CREATE, json_connection_id(packet_connection_uuid)), - ] - check_events(events_collector, expected_events) + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(devices) - # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- - events_collector.stop() + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(links) + response = context_client.ListServices(ContextId(**context_id)) + LOGGER.info('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) + assert len(response.services) == 1 # L3NM + for service in response.services: + service_id = service.service_id + response = context_client.ListConnections(service_id) + LOGGER.info(' ServiceId[{:s}] => Connections[{:d}] = {:s}'.format( + grpc_message_to_json_string(service_id), len(response.connections), + grpc_message_to_json_string(response))) + assert len(response.connections) == 1 # one connection per service -def test_scenario_service_created(context_client : ContextClient): # pylint: disable=redefined-outer-name # ----- List entities - Ensure service is created ------------------------------------------------------------------ - response = context_client.ListContexts(Empty()) - assert len(response.contexts) == len(CONTEXTS) - - response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) - assert len(response.topologies) == len(TOPOLOGIES) - - response = context_client.ListDevices(Empty()) - assert len(response.devices) == len(DEVICES) - - response = context_client.ListLinks(Empty()) - assert len(response.links) == len(LINKS) - - response = context_client.ListServices(ContextId(**CONTEXT_ID)) - LOGGER.info('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) - assert len(response.services) == 2 # L3NM + TAPI - for service in response.services: - service_id = service.service_id - response = context_client.ListConnections(service_id) - LOGGER.info(' ServiceId[{:s}] => Connections[{:d}] = {:s}'.format( - grpc_message_to_json_string(service_id), len(response.connections), grpc_message_to_json_string(response))) - assert len(response.connections) == 1 # one connection per service + per_domain(D1_CONTEXTS, D1_TOPOLOGIES, D1_DEVICES, D1_LINKS, D1_CONTEXT_ID, d1_context_client) + per_domain(D2_CONTEXTS, D2_TOPOLOGIES, D2_DEVICES, D2_LINKS, D2_CONTEXT_ID, d2_context_client) -- GitLab From e474fbcd62a1595c3b52ba55be97fa4e1e1106a4 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Thu, 10 Mar 2022 14:43:08 +0100 Subject: [PATCH 014/353] Updated context with RPCs for Slice management --- proto/context.proto | 7 + src/compute/proto/context_pb2.py | 76 ++++++- src/context/proto/context_pb2.py | 76 ++++++- src/context/proto/context_pb2_grpc.py | 198 ++++++++++++++++++ src/device/proto/context_pb2.py | 76 ++++++- src/interdomain/proto/context_pb2.py | 76 ++++++- src/monitoring/proto/context_pb2.py | 76 ++++++- .../proto/context_pb2.py | 76 ++++++- .../proto/context_pb2.py | 76 ++++++- src/service/proto/context_pb2.py | 76 ++++++- src/slice/proto/context_pb2.py | 76 ++++++- src/webui/proto/context_pb2.py | 76 ++++++- 12 files changed, 885 insertions(+), 80 deletions(-) diff --git a/proto/context.proto b/proto/context.proto index 346708c43..fc9c208b5 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -53,6 +53,13 @@ service ContextService { rpc RemoveService (ServiceId ) returns ( Empty ) {} rpc GetServiceEvents (Empty ) returns (stream ServiceEvent ) {} + rpc ListSliceIds (ContextId ) returns ( SliceIdList ) {} + rpc ListSlices (ContextId ) returns ( SliceList ) {} + rpc GetSlice (SliceId ) returns ( Slice ) {} + rpc SetSlice (Slice ) returns ( SliceId ) {} + rpc RemoveSlice (SliceId ) returns ( Empty ) {} + rpc GetSliceEvents (Empty ) returns (stream SliceEvent ) {} + rpc ListConnectionIds (ServiceId ) returns ( ConnectionIdList) {} rpc ListConnections (ServiceId ) returns ( ConnectionList ) {} rpc GetConnection (ConnectionId) returns ( Connection ) {} diff --git a/src/compute/proto/context_pb2.py b/src/compute/proto/context_pb2.py index 3e1b4c54d..1033a89e5 100644 --- a/src/compute/proto/context_pb2.py +++ b/src/compute/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -2641,7 +2641,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=5270, - serialized_end=7363, + serialized_end=7685, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', @@ -2943,10 +2943,70 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='ListSliceIds', + full_name='context.ContextService.ListSliceIds', + index=30, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSlices', + full_name='context.ContextService.ListSlices', + index=31, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSlice', + full_name='context.ContextService.GetSlice', + index=32, + containing_service=None, + input_type=_SLICEID, + output_type=_SLICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetSlice', + full_name='context.ContextService.SetSlice', + index=33, + containing_service=None, + input_type=_SLICE, + output_type=_SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveSlice', + full_name='context.ContextService.RemoveSlice', + index=34, + containing_service=None, + input_type=_SLICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSliceEvents', + full_name='context.ContextService.GetSliceEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_SLICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), _descriptor.MethodDescriptor( name='ListConnectionIds', full_name='context.ContextService.ListConnectionIds', - index=30, + index=36, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONIDLIST, @@ -2956,7 +3016,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='ListConnections', full_name='context.ContextService.ListConnections', - index=31, + index=37, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONLIST, @@ -2966,7 +3026,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnection', full_name='context.ContextService.GetConnection', - index=32, + index=38, containing_service=None, input_type=_CONNECTIONID, output_type=_CONNECTION, @@ -2976,7 +3036,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='SetConnection', full_name='context.ContextService.SetConnection', - index=33, + index=39, containing_service=None, input_type=_CONNECTION, output_type=_CONNECTIONID, @@ -2986,7 +3046,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='RemoveConnection', full_name='context.ContextService.RemoveConnection', - index=34, + index=40, containing_service=None, input_type=_CONNECTIONID, output_type=_EMPTY, @@ -2996,7 +3056,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnectionEvents', full_name='context.ContextService.GetConnectionEvents', - index=35, + index=41, containing_service=None, input_type=_EMPTY, output_type=_CONNECTIONEVENT, diff --git a/src/context/proto/context_pb2.py b/src/context/proto/context_pb2.py index 3e1b4c54d..1033a89e5 100644 --- a/src/context/proto/context_pb2.py +++ b/src/context/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -2641,7 +2641,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=5270, - serialized_end=7363, + serialized_end=7685, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', @@ -2943,10 +2943,70 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='ListSliceIds', + full_name='context.ContextService.ListSliceIds', + index=30, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSlices', + full_name='context.ContextService.ListSlices', + index=31, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSlice', + full_name='context.ContextService.GetSlice', + index=32, + containing_service=None, + input_type=_SLICEID, + output_type=_SLICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetSlice', + full_name='context.ContextService.SetSlice', + index=33, + containing_service=None, + input_type=_SLICE, + output_type=_SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveSlice', + full_name='context.ContextService.RemoveSlice', + index=34, + containing_service=None, + input_type=_SLICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSliceEvents', + full_name='context.ContextService.GetSliceEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_SLICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), _descriptor.MethodDescriptor( name='ListConnectionIds', full_name='context.ContextService.ListConnectionIds', - index=30, + index=36, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONIDLIST, @@ -2956,7 +3016,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='ListConnections', full_name='context.ContextService.ListConnections', - index=31, + index=37, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONLIST, @@ -2966,7 +3026,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnection', full_name='context.ContextService.GetConnection', - index=32, + index=38, containing_service=None, input_type=_CONNECTIONID, output_type=_CONNECTION, @@ -2976,7 +3036,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='SetConnection', full_name='context.ContextService.SetConnection', - index=33, + index=39, containing_service=None, input_type=_CONNECTION, output_type=_CONNECTIONID, @@ -2986,7 +3046,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='RemoveConnection', full_name='context.ContextService.RemoveConnection', - index=34, + index=40, containing_service=None, input_type=_CONNECTIONID, output_type=_EMPTY, @@ -2996,7 +3056,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnectionEvents', full_name='context.ContextService.GetConnectionEvents', - index=35, + index=41, containing_service=None, input_type=_EMPTY, output_type=_CONNECTIONEVENT, diff --git a/src/context/proto/context_pb2_grpc.py b/src/context/proto/context_pb2_grpc.py index 14397184e..8b30e91f3 100644 --- a/src/context/proto/context_pb2_grpc.py +++ b/src/context/proto/context_pb2_grpc.py @@ -164,6 +164,36 @@ class ContextServiceStub(object): request_serializer=context__pb2.Empty.SerializeToString, response_deserializer=context__pb2.ServiceEvent.FromString, ) + self.ListSliceIds = channel.unary_unary( + '/context.ContextService/ListSliceIds', + request_serializer=context__pb2.ContextId.SerializeToString, + response_deserializer=context__pb2.SliceIdList.FromString, + ) + self.ListSlices = channel.unary_unary( + '/context.ContextService/ListSlices', + request_serializer=context__pb2.ContextId.SerializeToString, + response_deserializer=context__pb2.SliceList.FromString, + ) + self.GetSlice = channel.unary_unary( + '/context.ContextService/GetSlice', + request_serializer=context__pb2.SliceId.SerializeToString, + response_deserializer=context__pb2.Slice.FromString, + ) + self.SetSlice = channel.unary_unary( + '/context.ContextService/SetSlice', + request_serializer=context__pb2.Slice.SerializeToString, + response_deserializer=context__pb2.SliceId.FromString, + ) + self.RemoveSlice = channel.unary_unary( + '/context.ContextService/RemoveSlice', + request_serializer=context__pb2.SliceId.SerializeToString, + response_deserializer=context__pb2.Empty.FromString, + ) + self.GetSliceEvents = channel.unary_stream( + '/context.ContextService/GetSliceEvents', + request_serializer=context__pb2.Empty.SerializeToString, + response_deserializer=context__pb2.SliceEvent.FromString, + ) self.ListConnectionIds = channel.unary_unary( '/context.ContextService/ListConnectionIds', request_serializer=context__pb2.ServiceId.SerializeToString, @@ -379,6 +409,42 @@ class ContextServiceServicer(object): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def ListSliceIds(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 ListSlices(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 GetSlice(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 SetSlice(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 RemoveSlice(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 GetSliceEvents(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 ListConnectionIds(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -568,6 +634,36 @@ def add_ContextServiceServicer_to_server(servicer, server): request_deserializer=context__pb2.Empty.FromString, response_serializer=context__pb2.ServiceEvent.SerializeToString, ), + 'ListSliceIds': grpc.unary_unary_rpc_method_handler( + servicer.ListSliceIds, + request_deserializer=context__pb2.ContextId.FromString, + response_serializer=context__pb2.SliceIdList.SerializeToString, + ), + 'ListSlices': grpc.unary_unary_rpc_method_handler( + servicer.ListSlices, + request_deserializer=context__pb2.ContextId.FromString, + response_serializer=context__pb2.SliceList.SerializeToString, + ), + 'GetSlice': grpc.unary_unary_rpc_method_handler( + servicer.GetSlice, + request_deserializer=context__pb2.SliceId.FromString, + response_serializer=context__pb2.Slice.SerializeToString, + ), + 'SetSlice': grpc.unary_unary_rpc_method_handler( + servicer.SetSlice, + request_deserializer=context__pb2.Slice.FromString, + response_serializer=context__pb2.SliceId.SerializeToString, + ), + 'RemoveSlice': grpc.unary_unary_rpc_method_handler( + servicer.RemoveSlice, + request_deserializer=context__pb2.SliceId.FromString, + response_serializer=context__pb2.Empty.SerializeToString, + ), + 'GetSliceEvents': grpc.unary_stream_rpc_method_handler( + servicer.GetSliceEvents, + request_deserializer=context__pb2.Empty.FromString, + response_serializer=context__pb2.SliceEvent.SerializeToString, + ), 'ListConnectionIds': grpc.unary_unary_rpc_method_handler( servicer.ListConnectionIds, request_deserializer=context__pb2.ServiceId.FromString, @@ -1118,6 +1214,108 @@ class ContextService(object): options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod + def ListSliceIds(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, '/context.ContextService/ListSliceIds', + context__pb2.ContextId.SerializeToString, + context__pb2.SliceIdList.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListSlices(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, '/context.ContextService/ListSlices', + context__pb2.ContextId.SerializeToString, + context__pb2.SliceList.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def GetSlice(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, '/context.ContextService/GetSlice', + context__pb2.SliceId.SerializeToString, + context__pb2.Slice.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def SetSlice(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, '/context.ContextService/SetSlice', + context__pb2.Slice.SerializeToString, + context__pb2.SliceId.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def RemoveSlice(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, '/context.ContextService/RemoveSlice', + context__pb2.SliceId.SerializeToString, + context__pb2.Empty.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def GetSliceEvents(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_stream(request, target, '/context.ContextService/GetSliceEvents', + context__pb2.Empty.SerializeToString, + context__pb2.SliceEvent.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod def ListConnectionIds(request, target, diff --git a/src/device/proto/context_pb2.py b/src/device/proto/context_pb2.py index 3e1b4c54d..1033a89e5 100644 --- a/src/device/proto/context_pb2.py +++ b/src/device/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -2641,7 +2641,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=5270, - serialized_end=7363, + serialized_end=7685, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', @@ -2943,10 +2943,70 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='ListSliceIds', + full_name='context.ContextService.ListSliceIds', + index=30, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSlices', + full_name='context.ContextService.ListSlices', + index=31, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSlice', + full_name='context.ContextService.GetSlice', + index=32, + containing_service=None, + input_type=_SLICEID, + output_type=_SLICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetSlice', + full_name='context.ContextService.SetSlice', + index=33, + containing_service=None, + input_type=_SLICE, + output_type=_SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveSlice', + full_name='context.ContextService.RemoveSlice', + index=34, + containing_service=None, + input_type=_SLICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSliceEvents', + full_name='context.ContextService.GetSliceEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_SLICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), _descriptor.MethodDescriptor( name='ListConnectionIds', full_name='context.ContextService.ListConnectionIds', - index=30, + index=36, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONIDLIST, @@ -2956,7 +3016,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='ListConnections', full_name='context.ContextService.ListConnections', - index=31, + index=37, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONLIST, @@ -2966,7 +3026,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnection', full_name='context.ContextService.GetConnection', - index=32, + index=38, containing_service=None, input_type=_CONNECTIONID, output_type=_CONNECTION, @@ -2976,7 +3036,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='SetConnection', full_name='context.ContextService.SetConnection', - index=33, + index=39, containing_service=None, input_type=_CONNECTION, output_type=_CONNECTIONID, @@ -2986,7 +3046,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='RemoveConnection', full_name='context.ContextService.RemoveConnection', - index=34, + index=40, containing_service=None, input_type=_CONNECTIONID, output_type=_EMPTY, @@ -2996,7 +3056,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnectionEvents', full_name='context.ContextService.GetConnectionEvents', - index=35, + index=41, containing_service=None, input_type=_EMPTY, output_type=_CONNECTIONEVENT, diff --git a/src/interdomain/proto/context_pb2.py b/src/interdomain/proto/context_pb2.py index 3e1b4c54d..1033a89e5 100644 --- a/src/interdomain/proto/context_pb2.py +++ b/src/interdomain/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -2641,7 +2641,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=5270, - serialized_end=7363, + serialized_end=7685, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', @@ -2943,10 +2943,70 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='ListSliceIds', + full_name='context.ContextService.ListSliceIds', + index=30, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSlices', + full_name='context.ContextService.ListSlices', + index=31, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSlice', + full_name='context.ContextService.GetSlice', + index=32, + containing_service=None, + input_type=_SLICEID, + output_type=_SLICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetSlice', + full_name='context.ContextService.SetSlice', + index=33, + containing_service=None, + input_type=_SLICE, + output_type=_SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveSlice', + full_name='context.ContextService.RemoveSlice', + index=34, + containing_service=None, + input_type=_SLICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSliceEvents', + full_name='context.ContextService.GetSliceEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_SLICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), _descriptor.MethodDescriptor( name='ListConnectionIds', full_name='context.ContextService.ListConnectionIds', - index=30, + index=36, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONIDLIST, @@ -2956,7 +3016,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='ListConnections', full_name='context.ContextService.ListConnections', - index=31, + index=37, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONLIST, @@ -2966,7 +3026,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnection', full_name='context.ContextService.GetConnection', - index=32, + index=38, containing_service=None, input_type=_CONNECTIONID, output_type=_CONNECTION, @@ -2976,7 +3036,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='SetConnection', full_name='context.ContextService.SetConnection', - index=33, + index=39, containing_service=None, input_type=_CONNECTION, output_type=_CONNECTIONID, @@ -2986,7 +3046,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='RemoveConnection', full_name='context.ContextService.RemoveConnection', - index=34, + index=40, containing_service=None, input_type=_CONNECTIONID, output_type=_EMPTY, @@ -2996,7 +3056,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnectionEvents', full_name='context.ContextService.GetConnectionEvents', - index=35, + index=41, containing_service=None, input_type=_EMPTY, output_type=_CONNECTIONEVENT, diff --git a/src/monitoring/proto/context_pb2.py b/src/monitoring/proto/context_pb2.py index 3e1b4c54d..1033a89e5 100644 --- a/src/monitoring/proto/context_pb2.py +++ b/src/monitoring/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -2641,7 +2641,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=5270, - serialized_end=7363, + serialized_end=7685, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', @@ -2943,10 +2943,70 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='ListSliceIds', + full_name='context.ContextService.ListSliceIds', + index=30, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSlices', + full_name='context.ContextService.ListSlices', + index=31, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSlice', + full_name='context.ContextService.GetSlice', + index=32, + containing_service=None, + input_type=_SLICEID, + output_type=_SLICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetSlice', + full_name='context.ContextService.SetSlice', + index=33, + containing_service=None, + input_type=_SLICE, + output_type=_SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveSlice', + full_name='context.ContextService.RemoveSlice', + index=34, + containing_service=None, + input_type=_SLICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSliceEvents', + full_name='context.ContextService.GetSliceEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_SLICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), _descriptor.MethodDescriptor( name='ListConnectionIds', full_name='context.ContextService.ListConnectionIds', - index=30, + index=36, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONIDLIST, @@ -2956,7 +3016,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='ListConnections', full_name='context.ContextService.ListConnections', - index=31, + index=37, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONLIST, @@ -2966,7 +3026,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnection', full_name='context.ContextService.GetConnection', - index=32, + index=38, containing_service=None, input_type=_CONNECTIONID, output_type=_CONNECTION, @@ -2976,7 +3036,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='SetConnection', full_name='context.ContextService.SetConnection', - index=33, + index=39, containing_service=None, input_type=_CONNECTION, output_type=_CONNECTIONID, @@ -2986,7 +3046,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='RemoveConnection', full_name='context.ContextService.RemoveConnection', - index=34, + index=40, containing_service=None, input_type=_CONNECTIONID, output_type=_EMPTY, @@ -2996,7 +3056,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnectionEvents', full_name='context.ContextService.GetConnectionEvents', - index=35, + index=41, containing_service=None, input_type=_EMPTY, output_type=_CONNECTIONEVENT, diff --git a/src/opticalattackmitigator/proto/context_pb2.py b/src/opticalattackmitigator/proto/context_pb2.py index 3e1b4c54d..1033a89e5 100644 --- a/src/opticalattackmitigator/proto/context_pb2.py +++ b/src/opticalattackmitigator/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -2641,7 +2641,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=5270, - serialized_end=7363, + serialized_end=7685, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', @@ -2943,10 +2943,70 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='ListSliceIds', + full_name='context.ContextService.ListSliceIds', + index=30, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSlices', + full_name='context.ContextService.ListSlices', + index=31, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSlice', + full_name='context.ContextService.GetSlice', + index=32, + containing_service=None, + input_type=_SLICEID, + output_type=_SLICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetSlice', + full_name='context.ContextService.SetSlice', + index=33, + containing_service=None, + input_type=_SLICE, + output_type=_SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveSlice', + full_name='context.ContextService.RemoveSlice', + index=34, + containing_service=None, + input_type=_SLICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSliceEvents', + full_name='context.ContextService.GetSliceEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_SLICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), _descriptor.MethodDescriptor( name='ListConnectionIds', full_name='context.ContextService.ListConnectionIds', - index=30, + index=36, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONIDLIST, @@ -2956,7 +3016,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='ListConnections', full_name='context.ContextService.ListConnections', - index=31, + index=37, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONLIST, @@ -2966,7 +3026,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnection', full_name='context.ContextService.GetConnection', - index=32, + index=38, containing_service=None, input_type=_CONNECTIONID, output_type=_CONNECTION, @@ -2976,7 +3036,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='SetConnection', full_name='context.ContextService.SetConnection', - index=33, + index=39, containing_service=None, input_type=_CONNECTION, output_type=_CONNECTIONID, @@ -2986,7 +3046,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='RemoveConnection', full_name='context.ContextService.RemoveConnection', - index=34, + index=40, containing_service=None, input_type=_CONNECTIONID, output_type=_EMPTY, @@ -2996,7 +3056,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnectionEvents', full_name='context.ContextService.GetConnectionEvents', - index=35, + index=41, containing_service=None, input_type=_EMPTY, output_type=_CONNECTIONEVENT, diff --git a/src/opticalcentralizedattackdetector/proto/context_pb2.py b/src/opticalcentralizedattackdetector/proto/context_pb2.py index 3e1b4c54d..1033a89e5 100644 --- a/src/opticalcentralizedattackdetector/proto/context_pb2.py +++ b/src/opticalcentralizedattackdetector/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -2641,7 +2641,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=5270, - serialized_end=7363, + serialized_end=7685, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', @@ -2943,10 +2943,70 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='ListSliceIds', + full_name='context.ContextService.ListSliceIds', + index=30, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSlices', + full_name='context.ContextService.ListSlices', + index=31, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSlice', + full_name='context.ContextService.GetSlice', + index=32, + containing_service=None, + input_type=_SLICEID, + output_type=_SLICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetSlice', + full_name='context.ContextService.SetSlice', + index=33, + containing_service=None, + input_type=_SLICE, + output_type=_SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveSlice', + full_name='context.ContextService.RemoveSlice', + index=34, + containing_service=None, + input_type=_SLICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSliceEvents', + full_name='context.ContextService.GetSliceEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_SLICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), _descriptor.MethodDescriptor( name='ListConnectionIds', full_name='context.ContextService.ListConnectionIds', - index=30, + index=36, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONIDLIST, @@ -2956,7 +3016,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='ListConnections', full_name='context.ContextService.ListConnections', - index=31, + index=37, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONLIST, @@ -2966,7 +3026,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnection', full_name='context.ContextService.GetConnection', - index=32, + index=38, containing_service=None, input_type=_CONNECTIONID, output_type=_CONNECTION, @@ -2976,7 +3036,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='SetConnection', full_name='context.ContextService.SetConnection', - index=33, + index=39, containing_service=None, input_type=_CONNECTION, output_type=_CONNECTIONID, @@ -2986,7 +3046,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='RemoveConnection', full_name='context.ContextService.RemoveConnection', - index=34, + index=40, containing_service=None, input_type=_CONNECTIONID, output_type=_EMPTY, @@ -2996,7 +3056,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnectionEvents', full_name='context.ContextService.GetConnectionEvents', - index=35, + index=41, containing_service=None, input_type=_EMPTY, output_type=_CONNECTIONEVENT, diff --git a/src/service/proto/context_pb2.py b/src/service/proto/context_pb2.py index 3e1b4c54d..1033a89e5 100644 --- a/src/service/proto/context_pb2.py +++ b/src/service/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -2641,7 +2641,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=5270, - serialized_end=7363, + serialized_end=7685, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', @@ -2943,10 +2943,70 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='ListSliceIds', + full_name='context.ContextService.ListSliceIds', + index=30, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSlices', + full_name='context.ContextService.ListSlices', + index=31, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSlice', + full_name='context.ContextService.GetSlice', + index=32, + containing_service=None, + input_type=_SLICEID, + output_type=_SLICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetSlice', + full_name='context.ContextService.SetSlice', + index=33, + containing_service=None, + input_type=_SLICE, + output_type=_SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveSlice', + full_name='context.ContextService.RemoveSlice', + index=34, + containing_service=None, + input_type=_SLICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSliceEvents', + full_name='context.ContextService.GetSliceEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_SLICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), _descriptor.MethodDescriptor( name='ListConnectionIds', full_name='context.ContextService.ListConnectionIds', - index=30, + index=36, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONIDLIST, @@ -2956,7 +3016,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='ListConnections', full_name='context.ContextService.ListConnections', - index=31, + index=37, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONLIST, @@ -2966,7 +3026,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnection', full_name='context.ContextService.GetConnection', - index=32, + index=38, containing_service=None, input_type=_CONNECTIONID, output_type=_CONNECTION, @@ -2976,7 +3036,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='SetConnection', full_name='context.ContextService.SetConnection', - index=33, + index=39, containing_service=None, input_type=_CONNECTION, output_type=_CONNECTIONID, @@ -2986,7 +3046,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='RemoveConnection', full_name='context.ContextService.RemoveConnection', - index=34, + index=40, containing_service=None, input_type=_CONNECTIONID, output_type=_EMPTY, @@ -2996,7 +3056,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnectionEvents', full_name='context.ContextService.GetConnectionEvents', - index=35, + index=41, containing_service=None, input_type=_EMPTY, output_type=_CONNECTIONEVENT, diff --git a/src/slice/proto/context_pb2.py b/src/slice/proto/context_pb2.py index 3e1b4c54d..1033a89e5 100644 --- a/src/slice/proto/context_pb2.py +++ b/src/slice/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -2641,7 +2641,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=5270, - serialized_end=7363, + serialized_end=7685, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', @@ -2943,10 +2943,70 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='ListSliceIds', + full_name='context.ContextService.ListSliceIds', + index=30, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSlices', + full_name='context.ContextService.ListSlices', + index=31, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSlice', + full_name='context.ContextService.GetSlice', + index=32, + containing_service=None, + input_type=_SLICEID, + output_type=_SLICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetSlice', + full_name='context.ContextService.SetSlice', + index=33, + containing_service=None, + input_type=_SLICE, + output_type=_SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveSlice', + full_name='context.ContextService.RemoveSlice', + index=34, + containing_service=None, + input_type=_SLICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSliceEvents', + full_name='context.ContextService.GetSliceEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_SLICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), _descriptor.MethodDescriptor( name='ListConnectionIds', full_name='context.ContextService.ListConnectionIds', - index=30, + index=36, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONIDLIST, @@ -2956,7 +3016,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='ListConnections', full_name='context.ContextService.ListConnections', - index=31, + index=37, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONLIST, @@ -2966,7 +3026,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnection', full_name='context.ContextService.GetConnection', - index=32, + index=38, containing_service=None, input_type=_CONNECTIONID, output_type=_CONNECTION, @@ -2976,7 +3036,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='SetConnection', full_name='context.ContextService.SetConnection', - index=33, + index=39, containing_service=None, input_type=_CONNECTION, output_type=_CONNECTIONID, @@ -2986,7 +3046,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='RemoveConnection', full_name='context.ContextService.RemoveConnection', - index=34, + index=40, containing_service=None, input_type=_CONNECTIONID, output_type=_EMPTY, @@ -2996,7 +3056,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnectionEvents', full_name='context.ContextService.GetConnectionEvents', - index=35, + index=41, containing_service=None, input_type=_EMPTY, output_type=_CONNECTIONEVENT, diff --git a/src/webui/proto/context_pb2.py b/src/webui/proto/context_pb2.py index 3e1b4c54d..1033a89e5 100644 --- a/src/webui/proto/context_pb2.py +++ b/src/webui/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xad\x10\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -2641,7 +2641,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=5270, - serialized_end=7363, + serialized_end=7685, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', @@ -2943,10 +2943,70 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='ListSliceIds', + full_name='context.ContextService.ListSliceIds', + index=30, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSlices', + full_name='context.ContextService.ListSlices', + index=31, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSlice', + full_name='context.ContextService.GetSlice', + index=32, + containing_service=None, + input_type=_SLICEID, + output_type=_SLICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetSlice', + full_name='context.ContextService.SetSlice', + index=33, + containing_service=None, + input_type=_SLICE, + output_type=_SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveSlice', + full_name='context.ContextService.RemoveSlice', + index=34, + containing_service=None, + input_type=_SLICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSliceEvents', + full_name='context.ContextService.GetSliceEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_SLICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), _descriptor.MethodDescriptor( name='ListConnectionIds', full_name='context.ContextService.ListConnectionIds', - index=30, + index=36, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONIDLIST, @@ -2956,7 +3016,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='ListConnections', full_name='context.ContextService.ListConnections', - index=31, + index=37, containing_service=None, input_type=_SERVICEID, output_type=_CONNECTIONLIST, @@ -2966,7 +3026,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnection', full_name='context.ContextService.GetConnection', - index=32, + index=38, containing_service=None, input_type=_CONNECTIONID, output_type=_CONNECTION, @@ -2976,7 +3036,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='SetConnection', full_name='context.ContextService.SetConnection', - index=33, + index=39, containing_service=None, input_type=_CONNECTION, output_type=_CONNECTIONID, @@ -2986,7 +3046,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='RemoveConnection', full_name='context.ContextService.RemoveConnection', - index=34, + index=40, containing_service=None, input_type=_CONNECTIONID, output_type=_EMPTY, @@ -2996,7 +3056,7 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='GetConnectionEvents', full_name='context.ContextService.GetConnectionEvents', - index=35, + index=41, containing_service=None, input_type=_EMPTY, output_type=_CONNECTIONEVENT, -- GitLab From 17719702c33076f52bf4c754f8d3fe12a4ecab32 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Thu, 10 Mar 2022 15:17:46 +0100 Subject: [PATCH 015/353] Updated Context proto file and related generated code --- proto/context.proto | 2 +- src/compute/proto/context_pb2.py | 100 +++++++++--------- src/context/proto/context_pb2.py | 100 +++++++++--------- src/device/proto/context_pb2.py | 100 +++++++++--------- src/interdomain/proto/context_pb2.py | 100 +++++++++--------- src/monitoring/proto/context_pb2.py | 100 +++++++++--------- .../proto/context_pb2.py | 100 +++++++++--------- .../proto/context_pb2.py | 100 +++++++++--------- src/service/proto/context_pb2.py | 100 +++++++++--------- src/slice/proto/context_pb2.py | 100 +++++++++--------- src/webui/proto/context_pb2.py | 100 +++++++++--------- 11 files changed, 501 insertions(+), 501 deletions(-) diff --git a/proto/context.proto b/proto/context.proto index fc9c208b5..bc0bfa0c6 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -270,7 +270,7 @@ message Slice { SliceId slice_id = 1; repeated EndPointId slice_endpoint_ids = 2; repeated Constraint slice_constraints = 3; - repeated ServiceId slice_services = 4; + repeated ServiceId slice_service_ids = 4; repeated SliceId slice_subslice_ids = 5; SliceStatus slice_status = 6; } diff --git a/src/compute/proto/context_pb2.py b/src/compute/proto/context_pb2.py index 1033a89e5..50d501d3a 100644 --- a/src/compute/proto/context_pb2.py +++ b/src/compute/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x95\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12-\n\x11slice_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4307, - serialized_end=4413, + serialized_start=4310, + serialized_end=4416, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4416, - serialized_end=4613, + serialized_start=4419, + serialized_end=4616, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4616, - serialized_end=4759, + serialized_start=4619, + serialized_end=4762, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4762, - serialized_end=4891, + serialized_start=4765, + serialized_end=4894, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,8 +204,8 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4894, - serialized_end=5030, + serialized_start=4897, + serialized_end=5033, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) @@ -245,8 +245,8 @@ _SLICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5033, - serialized_end=5172, + serialized_start=5036, + serialized_end=5175, ) _sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) @@ -276,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5174, - serialized_end=5267, + serialized_start=5177, + serialized_end=5270, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -1536,7 +1536,7 @@ _SLICE = _descriptor.Descriptor( is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( - name='slice_services', full_name='context.Slice.slice_services', index=3, + name='slice_service_ids', full_name='context.Slice.slice_service_ids', 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, @@ -1569,7 +1569,7 @@ _SLICE = _descriptor.Descriptor( oneofs=[ ], serialized_start=2691, - serialized_end=2965, + serialized_end=2968, ) @@ -1600,8 +1600,8 @@ _SLICESTATUS = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2967, - serialized_end=3028, + serialized_start=2970, + serialized_end=3031, ) @@ -1632,8 +1632,8 @@ _SLICEIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3030, - serialized_end=3080, + serialized_start=3033, + serialized_end=3083, ) @@ -1664,8 +1664,8 @@ _SLICELIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3082, - serialized_end=3125, + serialized_start=3085, + serialized_end=3128, ) @@ -1703,8 +1703,8 @@ _SLICEEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3127, - serialized_end=3206, + serialized_start=3130, + serialized_end=3209, ) @@ -1735,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3208, - serialized_end=3262, + serialized_start=3211, + serialized_end=3265, ) @@ -1788,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3265, - serialized_end=3461, + serialized_start=3268, + serialized_end=3464, ) @@ -1820,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3463, - serialized_end=3528, + serialized_start=3466, + serialized_end=3531, ) @@ -1852,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3530, - serialized_end=3588, + serialized_start=3533, + serialized_end=3591, ) @@ -1891,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3590, - serialized_end=3684, + serialized_start=3593, + serialized_end=3687, ) @@ -1937,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3687, - serialized_end=3817, + serialized_start=3690, + serialized_end=3820, ) @@ -1983,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3820, - serialized_end=3954, + serialized_start=3823, + serialized_end=3957, ) @@ -2029,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3956, - serialized_end=4057, + serialized_start=3959, + serialized_end=4060, ) @@ -2068,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4059, - serialized_end=4122, + serialized_start=4062, + serialized_end=4125, ) @@ -2114,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4124, - serialized_end=4218, + serialized_start=4127, + serialized_end=4221, ) @@ -2153,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4220, - serialized_end=4305, + serialized_start=4223, + serialized_end=4308, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -2213,7 +2213,7 @@ _SLICEID.fields_by_name['slice_uuid'].message_type = _UUID _SLICE.fields_by_name['slice_id'].message_type = _SLICEID _SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID _SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT -_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_service_ids'].message_type = _SERVICEID _SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID _SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS _SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM @@ -2640,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=5270, - serialized_end=7685, + serialized_start=5273, + serialized_end=7688, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/context/proto/context_pb2.py b/src/context/proto/context_pb2.py index 1033a89e5..50d501d3a 100644 --- a/src/context/proto/context_pb2.py +++ b/src/context/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x95\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12-\n\x11slice_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4307, - serialized_end=4413, + serialized_start=4310, + serialized_end=4416, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4416, - serialized_end=4613, + serialized_start=4419, + serialized_end=4616, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4616, - serialized_end=4759, + serialized_start=4619, + serialized_end=4762, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4762, - serialized_end=4891, + serialized_start=4765, + serialized_end=4894, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,8 +204,8 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4894, - serialized_end=5030, + serialized_start=4897, + serialized_end=5033, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) @@ -245,8 +245,8 @@ _SLICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5033, - serialized_end=5172, + serialized_start=5036, + serialized_end=5175, ) _sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) @@ -276,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5174, - serialized_end=5267, + serialized_start=5177, + serialized_end=5270, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -1536,7 +1536,7 @@ _SLICE = _descriptor.Descriptor( is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( - name='slice_services', full_name='context.Slice.slice_services', index=3, + name='slice_service_ids', full_name='context.Slice.slice_service_ids', 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, @@ -1569,7 +1569,7 @@ _SLICE = _descriptor.Descriptor( oneofs=[ ], serialized_start=2691, - serialized_end=2965, + serialized_end=2968, ) @@ -1600,8 +1600,8 @@ _SLICESTATUS = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2967, - serialized_end=3028, + serialized_start=2970, + serialized_end=3031, ) @@ -1632,8 +1632,8 @@ _SLICEIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3030, - serialized_end=3080, + serialized_start=3033, + serialized_end=3083, ) @@ -1664,8 +1664,8 @@ _SLICELIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3082, - serialized_end=3125, + serialized_start=3085, + serialized_end=3128, ) @@ -1703,8 +1703,8 @@ _SLICEEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3127, - serialized_end=3206, + serialized_start=3130, + serialized_end=3209, ) @@ -1735,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3208, - serialized_end=3262, + serialized_start=3211, + serialized_end=3265, ) @@ -1788,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3265, - serialized_end=3461, + serialized_start=3268, + serialized_end=3464, ) @@ -1820,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3463, - serialized_end=3528, + serialized_start=3466, + serialized_end=3531, ) @@ -1852,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3530, - serialized_end=3588, + serialized_start=3533, + serialized_end=3591, ) @@ -1891,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3590, - serialized_end=3684, + serialized_start=3593, + serialized_end=3687, ) @@ -1937,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3687, - serialized_end=3817, + serialized_start=3690, + serialized_end=3820, ) @@ -1983,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3820, - serialized_end=3954, + serialized_start=3823, + serialized_end=3957, ) @@ -2029,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3956, - serialized_end=4057, + serialized_start=3959, + serialized_end=4060, ) @@ -2068,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4059, - serialized_end=4122, + serialized_start=4062, + serialized_end=4125, ) @@ -2114,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4124, - serialized_end=4218, + serialized_start=4127, + serialized_end=4221, ) @@ -2153,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4220, - serialized_end=4305, + serialized_start=4223, + serialized_end=4308, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -2213,7 +2213,7 @@ _SLICEID.fields_by_name['slice_uuid'].message_type = _UUID _SLICE.fields_by_name['slice_id'].message_type = _SLICEID _SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID _SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT -_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_service_ids'].message_type = _SERVICEID _SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID _SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS _SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM @@ -2640,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=5270, - serialized_end=7685, + serialized_start=5273, + serialized_end=7688, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/device/proto/context_pb2.py b/src/device/proto/context_pb2.py index 1033a89e5..50d501d3a 100644 --- a/src/device/proto/context_pb2.py +++ b/src/device/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x95\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12-\n\x11slice_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4307, - serialized_end=4413, + serialized_start=4310, + serialized_end=4416, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4416, - serialized_end=4613, + serialized_start=4419, + serialized_end=4616, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4616, - serialized_end=4759, + serialized_start=4619, + serialized_end=4762, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4762, - serialized_end=4891, + serialized_start=4765, + serialized_end=4894, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,8 +204,8 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4894, - serialized_end=5030, + serialized_start=4897, + serialized_end=5033, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) @@ -245,8 +245,8 @@ _SLICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5033, - serialized_end=5172, + serialized_start=5036, + serialized_end=5175, ) _sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) @@ -276,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5174, - serialized_end=5267, + serialized_start=5177, + serialized_end=5270, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -1536,7 +1536,7 @@ _SLICE = _descriptor.Descriptor( is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( - name='slice_services', full_name='context.Slice.slice_services', index=3, + name='slice_service_ids', full_name='context.Slice.slice_service_ids', 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, @@ -1569,7 +1569,7 @@ _SLICE = _descriptor.Descriptor( oneofs=[ ], serialized_start=2691, - serialized_end=2965, + serialized_end=2968, ) @@ -1600,8 +1600,8 @@ _SLICESTATUS = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2967, - serialized_end=3028, + serialized_start=2970, + serialized_end=3031, ) @@ -1632,8 +1632,8 @@ _SLICEIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3030, - serialized_end=3080, + serialized_start=3033, + serialized_end=3083, ) @@ -1664,8 +1664,8 @@ _SLICELIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3082, - serialized_end=3125, + serialized_start=3085, + serialized_end=3128, ) @@ -1703,8 +1703,8 @@ _SLICEEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3127, - serialized_end=3206, + serialized_start=3130, + serialized_end=3209, ) @@ -1735,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3208, - serialized_end=3262, + serialized_start=3211, + serialized_end=3265, ) @@ -1788,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3265, - serialized_end=3461, + serialized_start=3268, + serialized_end=3464, ) @@ -1820,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3463, - serialized_end=3528, + serialized_start=3466, + serialized_end=3531, ) @@ -1852,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3530, - serialized_end=3588, + serialized_start=3533, + serialized_end=3591, ) @@ -1891,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3590, - serialized_end=3684, + serialized_start=3593, + serialized_end=3687, ) @@ -1937,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3687, - serialized_end=3817, + serialized_start=3690, + serialized_end=3820, ) @@ -1983,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3820, - serialized_end=3954, + serialized_start=3823, + serialized_end=3957, ) @@ -2029,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3956, - serialized_end=4057, + serialized_start=3959, + serialized_end=4060, ) @@ -2068,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4059, - serialized_end=4122, + serialized_start=4062, + serialized_end=4125, ) @@ -2114,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4124, - serialized_end=4218, + serialized_start=4127, + serialized_end=4221, ) @@ -2153,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4220, - serialized_end=4305, + serialized_start=4223, + serialized_end=4308, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -2213,7 +2213,7 @@ _SLICEID.fields_by_name['slice_uuid'].message_type = _UUID _SLICE.fields_by_name['slice_id'].message_type = _SLICEID _SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID _SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT -_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_service_ids'].message_type = _SERVICEID _SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID _SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS _SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM @@ -2640,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=5270, - serialized_end=7685, + serialized_start=5273, + serialized_end=7688, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/interdomain/proto/context_pb2.py b/src/interdomain/proto/context_pb2.py index 1033a89e5..50d501d3a 100644 --- a/src/interdomain/proto/context_pb2.py +++ b/src/interdomain/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x95\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12-\n\x11slice_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4307, - serialized_end=4413, + serialized_start=4310, + serialized_end=4416, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4416, - serialized_end=4613, + serialized_start=4419, + serialized_end=4616, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4616, - serialized_end=4759, + serialized_start=4619, + serialized_end=4762, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4762, - serialized_end=4891, + serialized_start=4765, + serialized_end=4894, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,8 +204,8 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4894, - serialized_end=5030, + serialized_start=4897, + serialized_end=5033, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) @@ -245,8 +245,8 @@ _SLICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5033, - serialized_end=5172, + serialized_start=5036, + serialized_end=5175, ) _sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) @@ -276,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5174, - serialized_end=5267, + serialized_start=5177, + serialized_end=5270, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -1536,7 +1536,7 @@ _SLICE = _descriptor.Descriptor( is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( - name='slice_services', full_name='context.Slice.slice_services', index=3, + name='slice_service_ids', full_name='context.Slice.slice_service_ids', 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, @@ -1569,7 +1569,7 @@ _SLICE = _descriptor.Descriptor( oneofs=[ ], serialized_start=2691, - serialized_end=2965, + serialized_end=2968, ) @@ -1600,8 +1600,8 @@ _SLICESTATUS = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2967, - serialized_end=3028, + serialized_start=2970, + serialized_end=3031, ) @@ -1632,8 +1632,8 @@ _SLICEIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3030, - serialized_end=3080, + serialized_start=3033, + serialized_end=3083, ) @@ -1664,8 +1664,8 @@ _SLICELIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3082, - serialized_end=3125, + serialized_start=3085, + serialized_end=3128, ) @@ -1703,8 +1703,8 @@ _SLICEEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3127, - serialized_end=3206, + serialized_start=3130, + serialized_end=3209, ) @@ -1735,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3208, - serialized_end=3262, + serialized_start=3211, + serialized_end=3265, ) @@ -1788,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3265, - serialized_end=3461, + serialized_start=3268, + serialized_end=3464, ) @@ -1820,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3463, - serialized_end=3528, + serialized_start=3466, + serialized_end=3531, ) @@ -1852,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3530, - serialized_end=3588, + serialized_start=3533, + serialized_end=3591, ) @@ -1891,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3590, - serialized_end=3684, + serialized_start=3593, + serialized_end=3687, ) @@ -1937,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3687, - serialized_end=3817, + serialized_start=3690, + serialized_end=3820, ) @@ -1983,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3820, - serialized_end=3954, + serialized_start=3823, + serialized_end=3957, ) @@ -2029,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3956, - serialized_end=4057, + serialized_start=3959, + serialized_end=4060, ) @@ -2068,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4059, - serialized_end=4122, + serialized_start=4062, + serialized_end=4125, ) @@ -2114,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4124, - serialized_end=4218, + serialized_start=4127, + serialized_end=4221, ) @@ -2153,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4220, - serialized_end=4305, + serialized_start=4223, + serialized_end=4308, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -2213,7 +2213,7 @@ _SLICEID.fields_by_name['slice_uuid'].message_type = _UUID _SLICE.fields_by_name['slice_id'].message_type = _SLICEID _SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID _SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT -_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_service_ids'].message_type = _SERVICEID _SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID _SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS _SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM @@ -2640,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=5270, - serialized_end=7685, + serialized_start=5273, + serialized_end=7688, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/monitoring/proto/context_pb2.py b/src/monitoring/proto/context_pb2.py index 1033a89e5..50d501d3a 100644 --- a/src/monitoring/proto/context_pb2.py +++ b/src/monitoring/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x95\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12-\n\x11slice_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4307, - serialized_end=4413, + serialized_start=4310, + serialized_end=4416, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4416, - serialized_end=4613, + serialized_start=4419, + serialized_end=4616, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4616, - serialized_end=4759, + serialized_start=4619, + serialized_end=4762, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4762, - serialized_end=4891, + serialized_start=4765, + serialized_end=4894, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,8 +204,8 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4894, - serialized_end=5030, + serialized_start=4897, + serialized_end=5033, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) @@ -245,8 +245,8 @@ _SLICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5033, - serialized_end=5172, + serialized_start=5036, + serialized_end=5175, ) _sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) @@ -276,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5174, - serialized_end=5267, + serialized_start=5177, + serialized_end=5270, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -1536,7 +1536,7 @@ _SLICE = _descriptor.Descriptor( is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( - name='slice_services', full_name='context.Slice.slice_services', index=3, + name='slice_service_ids', full_name='context.Slice.slice_service_ids', 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, @@ -1569,7 +1569,7 @@ _SLICE = _descriptor.Descriptor( oneofs=[ ], serialized_start=2691, - serialized_end=2965, + serialized_end=2968, ) @@ -1600,8 +1600,8 @@ _SLICESTATUS = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2967, - serialized_end=3028, + serialized_start=2970, + serialized_end=3031, ) @@ -1632,8 +1632,8 @@ _SLICEIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3030, - serialized_end=3080, + serialized_start=3033, + serialized_end=3083, ) @@ -1664,8 +1664,8 @@ _SLICELIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3082, - serialized_end=3125, + serialized_start=3085, + serialized_end=3128, ) @@ -1703,8 +1703,8 @@ _SLICEEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3127, - serialized_end=3206, + serialized_start=3130, + serialized_end=3209, ) @@ -1735,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3208, - serialized_end=3262, + serialized_start=3211, + serialized_end=3265, ) @@ -1788,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3265, - serialized_end=3461, + serialized_start=3268, + serialized_end=3464, ) @@ -1820,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3463, - serialized_end=3528, + serialized_start=3466, + serialized_end=3531, ) @@ -1852,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3530, - serialized_end=3588, + serialized_start=3533, + serialized_end=3591, ) @@ -1891,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3590, - serialized_end=3684, + serialized_start=3593, + serialized_end=3687, ) @@ -1937,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3687, - serialized_end=3817, + serialized_start=3690, + serialized_end=3820, ) @@ -1983,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3820, - serialized_end=3954, + serialized_start=3823, + serialized_end=3957, ) @@ -2029,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3956, - serialized_end=4057, + serialized_start=3959, + serialized_end=4060, ) @@ -2068,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4059, - serialized_end=4122, + serialized_start=4062, + serialized_end=4125, ) @@ -2114,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4124, - serialized_end=4218, + serialized_start=4127, + serialized_end=4221, ) @@ -2153,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4220, - serialized_end=4305, + serialized_start=4223, + serialized_end=4308, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -2213,7 +2213,7 @@ _SLICEID.fields_by_name['slice_uuid'].message_type = _UUID _SLICE.fields_by_name['slice_id'].message_type = _SLICEID _SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID _SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT -_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_service_ids'].message_type = _SERVICEID _SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID _SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS _SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM @@ -2640,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=5270, - serialized_end=7685, + serialized_start=5273, + serialized_end=7688, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/opticalattackmitigator/proto/context_pb2.py b/src/opticalattackmitigator/proto/context_pb2.py index 1033a89e5..50d501d3a 100644 --- a/src/opticalattackmitigator/proto/context_pb2.py +++ b/src/opticalattackmitigator/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x95\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12-\n\x11slice_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4307, - serialized_end=4413, + serialized_start=4310, + serialized_end=4416, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4416, - serialized_end=4613, + serialized_start=4419, + serialized_end=4616, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4616, - serialized_end=4759, + serialized_start=4619, + serialized_end=4762, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4762, - serialized_end=4891, + serialized_start=4765, + serialized_end=4894, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,8 +204,8 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4894, - serialized_end=5030, + serialized_start=4897, + serialized_end=5033, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) @@ -245,8 +245,8 @@ _SLICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5033, - serialized_end=5172, + serialized_start=5036, + serialized_end=5175, ) _sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) @@ -276,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5174, - serialized_end=5267, + serialized_start=5177, + serialized_end=5270, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -1536,7 +1536,7 @@ _SLICE = _descriptor.Descriptor( is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( - name='slice_services', full_name='context.Slice.slice_services', index=3, + name='slice_service_ids', full_name='context.Slice.slice_service_ids', 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, @@ -1569,7 +1569,7 @@ _SLICE = _descriptor.Descriptor( oneofs=[ ], serialized_start=2691, - serialized_end=2965, + serialized_end=2968, ) @@ -1600,8 +1600,8 @@ _SLICESTATUS = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2967, - serialized_end=3028, + serialized_start=2970, + serialized_end=3031, ) @@ -1632,8 +1632,8 @@ _SLICEIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3030, - serialized_end=3080, + serialized_start=3033, + serialized_end=3083, ) @@ -1664,8 +1664,8 @@ _SLICELIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3082, - serialized_end=3125, + serialized_start=3085, + serialized_end=3128, ) @@ -1703,8 +1703,8 @@ _SLICEEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3127, - serialized_end=3206, + serialized_start=3130, + serialized_end=3209, ) @@ -1735,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3208, - serialized_end=3262, + serialized_start=3211, + serialized_end=3265, ) @@ -1788,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3265, - serialized_end=3461, + serialized_start=3268, + serialized_end=3464, ) @@ -1820,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3463, - serialized_end=3528, + serialized_start=3466, + serialized_end=3531, ) @@ -1852,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3530, - serialized_end=3588, + serialized_start=3533, + serialized_end=3591, ) @@ -1891,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3590, - serialized_end=3684, + serialized_start=3593, + serialized_end=3687, ) @@ -1937,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3687, - serialized_end=3817, + serialized_start=3690, + serialized_end=3820, ) @@ -1983,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3820, - serialized_end=3954, + serialized_start=3823, + serialized_end=3957, ) @@ -2029,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3956, - serialized_end=4057, + serialized_start=3959, + serialized_end=4060, ) @@ -2068,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4059, - serialized_end=4122, + serialized_start=4062, + serialized_end=4125, ) @@ -2114,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4124, - serialized_end=4218, + serialized_start=4127, + serialized_end=4221, ) @@ -2153,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4220, - serialized_end=4305, + serialized_start=4223, + serialized_end=4308, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -2213,7 +2213,7 @@ _SLICEID.fields_by_name['slice_uuid'].message_type = _UUID _SLICE.fields_by_name['slice_id'].message_type = _SLICEID _SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID _SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT -_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_service_ids'].message_type = _SERVICEID _SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID _SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS _SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM @@ -2640,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=5270, - serialized_end=7685, + serialized_start=5273, + serialized_end=7688, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/opticalcentralizedattackdetector/proto/context_pb2.py b/src/opticalcentralizedattackdetector/proto/context_pb2.py index 1033a89e5..50d501d3a 100644 --- a/src/opticalcentralizedattackdetector/proto/context_pb2.py +++ b/src/opticalcentralizedattackdetector/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x95\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12-\n\x11slice_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4307, - serialized_end=4413, + serialized_start=4310, + serialized_end=4416, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4416, - serialized_end=4613, + serialized_start=4419, + serialized_end=4616, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4616, - serialized_end=4759, + serialized_start=4619, + serialized_end=4762, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4762, - serialized_end=4891, + serialized_start=4765, + serialized_end=4894, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,8 +204,8 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4894, - serialized_end=5030, + serialized_start=4897, + serialized_end=5033, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) @@ -245,8 +245,8 @@ _SLICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5033, - serialized_end=5172, + serialized_start=5036, + serialized_end=5175, ) _sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) @@ -276,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5174, - serialized_end=5267, + serialized_start=5177, + serialized_end=5270, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -1536,7 +1536,7 @@ _SLICE = _descriptor.Descriptor( is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( - name='slice_services', full_name='context.Slice.slice_services', index=3, + name='slice_service_ids', full_name='context.Slice.slice_service_ids', 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, @@ -1569,7 +1569,7 @@ _SLICE = _descriptor.Descriptor( oneofs=[ ], serialized_start=2691, - serialized_end=2965, + serialized_end=2968, ) @@ -1600,8 +1600,8 @@ _SLICESTATUS = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2967, - serialized_end=3028, + serialized_start=2970, + serialized_end=3031, ) @@ -1632,8 +1632,8 @@ _SLICEIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3030, - serialized_end=3080, + serialized_start=3033, + serialized_end=3083, ) @@ -1664,8 +1664,8 @@ _SLICELIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3082, - serialized_end=3125, + serialized_start=3085, + serialized_end=3128, ) @@ -1703,8 +1703,8 @@ _SLICEEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3127, - serialized_end=3206, + serialized_start=3130, + serialized_end=3209, ) @@ -1735,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3208, - serialized_end=3262, + serialized_start=3211, + serialized_end=3265, ) @@ -1788,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3265, - serialized_end=3461, + serialized_start=3268, + serialized_end=3464, ) @@ -1820,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3463, - serialized_end=3528, + serialized_start=3466, + serialized_end=3531, ) @@ -1852,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3530, - serialized_end=3588, + serialized_start=3533, + serialized_end=3591, ) @@ -1891,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3590, - serialized_end=3684, + serialized_start=3593, + serialized_end=3687, ) @@ -1937,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3687, - serialized_end=3817, + serialized_start=3690, + serialized_end=3820, ) @@ -1983,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3820, - serialized_end=3954, + serialized_start=3823, + serialized_end=3957, ) @@ -2029,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3956, - serialized_end=4057, + serialized_start=3959, + serialized_end=4060, ) @@ -2068,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4059, - serialized_end=4122, + serialized_start=4062, + serialized_end=4125, ) @@ -2114,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4124, - serialized_end=4218, + serialized_start=4127, + serialized_end=4221, ) @@ -2153,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4220, - serialized_end=4305, + serialized_start=4223, + serialized_end=4308, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -2213,7 +2213,7 @@ _SLICEID.fields_by_name['slice_uuid'].message_type = _UUID _SLICE.fields_by_name['slice_id'].message_type = _SLICEID _SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID _SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT -_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_service_ids'].message_type = _SERVICEID _SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID _SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS _SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM @@ -2640,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=5270, - serialized_end=7685, + serialized_start=5273, + serialized_end=7688, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/service/proto/context_pb2.py b/src/service/proto/context_pb2.py index 1033a89e5..50d501d3a 100644 --- a/src/service/proto/context_pb2.py +++ b/src/service/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x95\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12-\n\x11slice_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4307, - serialized_end=4413, + serialized_start=4310, + serialized_end=4416, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4416, - serialized_end=4613, + serialized_start=4419, + serialized_end=4616, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4616, - serialized_end=4759, + serialized_start=4619, + serialized_end=4762, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4762, - serialized_end=4891, + serialized_start=4765, + serialized_end=4894, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,8 +204,8 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4894, - serialized_end=5030, + serialized_start=4897, + serialized_end=5033, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) @@ -245,8 +245,8 @@ _SLICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5033, - serialized_end=5172, + serialized_start=5036, + serialized_end=5175, ) _sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) @@ -276,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5174, - serialized_end=5267, + serialized_start=5177, + serialized_end=5270, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -1536,7 +1536,7 @@ _SLICE = _descriptor.Descriptor( is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( - name='slice_services', full_name='context.Slice.slice_services', index=3, + name='slice_service_ids', full_name='context.Slice.slice_service_ids', 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, @@ -1569,7 +1569,7 @@ _SLICE = _descriptor.Descriptor( oneofs=[ ], serialized_start=2691, - serialized_end=2965, + serialized_end=2968, ) @@ -1600,8 +1600,8 @@ _SLICESTATUS = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2967, - serialized_end=3028, + serialized_start=2970, + serialized_end=3031, ) @@ -1632,8 +1632,8 @@ _SLICEIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3030, - serialized_end=3080, + serialized_start=3033, + serialized_end=3083, ) @@ -1664,8 +1664,8 @@ _SLICELIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3082, - serialized_end=3125, + serialized_start=3085, + serialized_end=3128, ) @@ -1703,8 +1703,8 @@ _SLICEEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3127, - serialized_end=3206, + serialized_start=3130, + serialized_end=3209, ) @@ -1735,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3208, - serialized_end=3262, + serialized_start=3211, + serialized_end=3265, ) @@ -1788,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3265, - serialized_end=3461, + serialized_start=3268, + serialized_end=3464, ) @@ -1820,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3463, - serialized_end=3528, + serialized_start=3466, + serialized_end=3531, ) @@ -1852,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3530, - serialized_end=3588, + serialized_start=3533, + serialized_end=3591, ) @@ -1891,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3590, - serialized_end=3684, + serialized_start=3593, + serialized_end=3687, ) @@ -1937,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3687, - serialized_end=3817, + serialized_start=3690, + serialized_end=3820, ) @@ -1983,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3820, - serialized_end=3954, + serialized_start=3823, + serialized_end=3957, ) @@ -2029,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3956, - serialized_end=4057, + serialized_start=3959, + serialized_end=4060, ) @@ -2068,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4059, - serialized_end=4122, + serialized_start=4062, + serialized_end=4125, ) @@ -2114,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4124, - serialized_end=4218, + serialized_start=4127, + serialized_end=4221, ) @@ -2153,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4220, - serialized_end=4305, + serialized_start=4223, + serialized_end=4308, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -2213,7 +2213,7 @@ _SLICEID.fields_by_name['slice_uuid'].message_type = _UUID _SLICE.fields_by_name['slice_id'].message_type = _SLICEID _SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID _SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT -_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_service_ids'].message_type = _SERVICEID _SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID _SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS _SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM @@ -2640,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=5270, - serialized_end=7685, + serialized_start=5273, + serialized_end=7688, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/slice/proto/context_pb2.py b/src/slice/proto/context_pb2.py index 1033a89e5..50d501d3a 100644 --- a/src/slice/proto/context_pb2.py +++ b/src/slice/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x95\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12-\n\x11slice_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4307, - serialized_end=4413, + serialized_start=4310, + serialized_end=4416, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4416, - serialized_end=4613, + serialized_start=4419, + serialized_end=4616, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4616, - serialized_end=4759, + serialized_start=4619, + serialized_end=4762, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4762, - serialized_end=4891, + serialized_start=4765, + serialized_end=4894, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,8 +204,8 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4894, - serialized_end=5030, + serialized_start=4897, + serialized_end=5033, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) @@ -245,8 +245,8 @@ _SLICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5033, - serialized_end=5172, + serialized_start=5036, + serialized_end=5175, ) _sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) @@ -276,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5174, - serialized_end=5267, + serialized_start=5177, + serialized_end=5270, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -1536,7 +1536,7 @@ _SLICE = _descriptor.Descriptor( is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( - name='slice_services', full_name='context.Slice.slice_services', index=3, + name='slice_service_ids', full_name='context.Slice.slice_service_ids', 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, @@ -1569,7 +1569,7 @@ _SLICE = _descriptor.Descriptor( oneofs=[ ], serialized_start=2691, - serialized_end=2965, + serialized_end=2968, ) @@ -1600,8 +1600,8 @@ _SLICESTATUS = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2967, - serialized_end=3028, + serialized_start=2970, + serialized_end=3031, ) @@ -1632,8 +1632,8 @@ _SLICEIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3030, - serialized_end=3080, + serialized_start=3033, + serialized_end=3083, ) @@ -1664,8 +1664,8 @@ _SLICELIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3082, - serialized_end=3125, + serialized_start=3085, + serialized_end=3128, ) @@ -1703,8 +1703,8 @@ _SLICEEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3127, - serialized_end=3206, + serialized_start=3130, + serialized_end=3209, ) @@ -1735,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3208, - serialized_end=3262, + serialized_start=3211, + serialized_end=3265, ) @@ -1788,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3265, - serialized_end=3461, + serialized_start=3268, + serialized_end=3464, ) @@ -1820,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3463, - serialized_end=3528, + serialized_start=3466, + serialized_end=3531, ) @@ -1852,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3530, - serialized_end=3588, + serialized_start=3533, + serialized_end=3591, ) @@ -1891,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3590, - serialized_end=3684, + serialized_start=3593, + serialized_end=3687, ) @@ -1937,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3687, - serialized_end=3817, + serialized_start=3690, + serialized_end=3820, ) @@ -1983,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3820, - serialized_end=3954, + serialized_start=3823, + serialized_end=3957, ) @@ -2029,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3956, - serialized_end=4057, + serialized_start=3959, + serialized_end=4060, ) @@ -2068,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4059, - serialized_end=4122, + serialized_start=4062, + serialized_end=4125, ) @@ -2114,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4124, - serialized_end=4218, + serialized_start=4127, + serialized_end=4221, ) @@ -2153,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4220, - serialized_end=4305, + serialized_start=4223, + serialized_end=4308, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -2213,7 +2213,7 @@ _SLICEID.fields_by_name['slice_uuid'].message_type = _UUID _SLICE.fields_by_name['slice_id'].message_type = _SLICEID _SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID _SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT -_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_service_ids'].message_type = _SERVICEID _SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID _SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS _SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM @@ -2640,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=5270, - serialized_end=7685, + serialized_start=5273, + serialized_end=7688, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', diff --git a/src/webui/proto/context_pb2.py b/src/webui/proto/context_pb2.py index 1033a89e5..50d501d3a 100644 --- a/src/webui/proto/context_pb2.py +++ b/src/webui/proto/context_pb2.py @@ -21,7 +21,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x92\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12*\n\x0eslice_services\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x95\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12-\n\x11slice_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' , dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) @@ -55,8 +55,8 @@ _EVENTTYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4307, - serialized_end=4413, + serialized_start=4310, + serialized_end=4416, ) _sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) @@ -101,8 +101,8 @@ _DEVICEDRIVERENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4416, - serialized_end=4613, + serialized_start=4419, + serialized_end=4616, ) _sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) @@ -132,8 +132,8 @@ _DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4616, - serialized_end=4759, + serialized_start=4619, + serialized_end=4762, ) _sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) @@ -168,8 +168,8 @@ _SERVICETYPEENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4762, - serialized_end=4891, + serialized_start=4765, + serialized_end=4894, ) _sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) @@ -204,8 +204,8 @@ _SERVICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=4894, - serialized_end=5030, + serialized_start=4897, + serialized_end=5033, ) _sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) @@ -245,8 +245,8 @@ _SLICESTATUSENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5033, - serialized_end=5172, + serialized_start=5036, + serialized_end=5175, ) _sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) @@ -276,8 +276,8 @@ _CONFIGACTIONENUM = _descriptor.EnumDescriptor( ], containing_type=None, serialized_options=None, - serialized_start=5174, - serialized_end=5267, + serialized_start=5177, + serialized_end=5270, ) _sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) @@ -1536,7 +1536,7 @@ _SLICE = _descriptor.Descriptor( is_extension=False, extension_scope=None, serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), _descriptor.FieldDescriptor( - name='slice_services', full_name='context.Slice.slice_services', index=3, + name='slice_service_ids', full_name='context.Slice.slice_service_ids', 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, @@ -1569,7 +1569,7 @@ _SLICE = _descriptor.Descriptor( oneofs=[ ], serialized_start=2691, - serialized_end=2965, + serialized_end=2968, ) @@ -1600,8 +1600,8 @@ _SLICESTATUS = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=2967, - serialized_end=3028, + serialized_start=2970, + serialized_end=3031, ) @@ -1632,8 +1632,8 @@ _SLICEIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3030, - serialized_end=3080, + serialized_start=3033, + serialized_end=3083, ) @@ -1664,8 +1664,8 @@ _SLICELIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3082, - serialized_end=3125, + serialized_start=3085, + serialized_end=3128, ) @@ -1703,8 +1703,8 @@ _SLICEEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3127, - serialized_end=3206, + serialized_start=3130, + serialized_end=3209, ) @@ -1735,8 +1735,8 @@ _CONNECTIONID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3208, - serialized_end=3262, + serialized_start=3211, + serialized_end=3265, ) @@ -1788,8 +1788,8 @@ _CONNECTION = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3265, - serialized_end=3461, + serialized_start=3268, + serialized_end=3464, ) @@ -1820,8 +1820,8 @@ _CONNECTIONIDLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3463, - serialized_end=3528, + serialized_start=3466, + serialized_end=3531, ) @@ -1852,8 +1852,8 @@ _CONNECTIONLIST = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3530, - serialized_end=3588, + serialized_start=3533, + serialized_end=3591, ) @@ -1891,8 +1891,8 @@ _CONNECTIONEVENT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3590, - serialized_end=3684, + serialized_start=3593, + serialized_end=3687, ) @@ -1937,8 +1937,8 @@ _ENDPOINTID = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3687, - serialized_end=3817, + serialized_start=3690, + serialized_end=3820, ) @@ -1983,8 +1983,8 @@ _ENDPOINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3820, - serialized_end=3954, + serialized_start=3823, + serialized_end=3957, ) @@ -2029,8 +2029,8 @@ _CONFIGRULE = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=3956, - serialized_end=4057, + serialized_start=3959, + serialized_end=4060, ) @@ -2068,8 +2068,8 @@ _CONSTRAINT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4059, - serialized_end=4122, + serialized_start=4062, + serialized_end=4125, ) @@ -2114,8 +2114,8 @@ _TERAFLOWCONTROLLER = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4124, - serialized_end=4218, + serialized_start=4127, + serialized_end=4221, ) @@ -2153,8 +2153,8 @@ _AUTHENTICATIONRESULT = _descriptor.Descriptor( extension_ranges=[], oneofs=[ ], - serialized_start=4220, - serialized_end=4305, + serialized_start=4223, + serialized_end=4308, ) _EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM @@ -2213,7 +2213,7 @@ _SLICEID.fields_by_name['slice_uuid'].message_type = _UUID _SLICE.fields_by_name['slice_id'].message_type = _SLICEID _SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID _SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT -_SLICE.fields_by_name['slice_services'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_service_ids'].message_type = _SERVICEID _SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID _SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS _SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM @@ -2640,8 +2640,8 @@ _CONTEXTSERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=5270, - serialized_end=7685, + serialized_start=5273, + serialized_end=7688, methods=[ _descriptor.MethodDescriptor( name='ListContextIds', -- GitLab From 400bc52559eb286051902e893cf63513d23ceafa Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Thu, 10 Mar 2022 15:18:27 +0100 Subject: [PATCH 016/353] Context: - Implemented support for Slices - missing: unitary tests --- .../service/database/RelationModels.py | 16 ++ src/context/service/database/SliceModel.py | 85 +++++++++ src/context/service/grpc_server/Constants.py | 5 +- .../grpc_server/ContextServiceServicerImpl.py | 161 +++++++++++++++++- 4 files changed, 258 insertions(+), 9 deletions(-) create mode 100644 src/context/service/database/SliceModel.py diff --git a/src/context/service/database/RelationModels.py b/src/context/service/database/RelationModels.py index 20e0fc450..98b077a77 100644 --- a/src/context/service/database/RelationModels.py +++ b/src/context/service/database/RelationModels.py @@ -21,6 +21,7 @@ from .DeviceModel import DeviceModel from .EndPointModel import EndPointModel from .LinkModel import LinkModel from .ServiceModel import ServiceModel +from .SliceModel import SliceModel from .TopologyModel import TopologyModel LOGGER = logging.getLogger(__name__) @@ -40,6 +41,21 @@ class ServiceEndPointModel(Model): # pylint: disable=abstract-method service_fk = ForeignKeyField(ServiceModel) endpoint_fk = ForeignKeyField(EndPointModel) +class SliceEndPointModel(Model): # pylint: disable=abstract-method + pk = PrimaryKeyField() + slice_fk = ForeignKeyField(SliceModel) + endpoint_fk = ForeignKeyField(EndPointModel) + +class SliceServiceModel(Model): # pylint: disable=abstract-method + pk = PrimaryKeyField() + slice_fk = ForeignKeyField(SliceModel) + service_fk = ForeignKeyField(ServiceModel) + +class SliceSubSliceModel(Model): # pylint: disable=abstract-method + pk = PrimaryKeyField() + slice_fk = ForeignKeyField(SliceModel) + sub_slice_fk = ForeignKeyField(SliceModel) + class TopologyDeviceModel(Model): # pylint: disable=abstract-method pk = PrimaryKeyField() topology_fk = ForeignKeyField(TopologyModel) diff --git a/src/context/service/database/SliceModel.py b/src/context/service/database/SliceModel.py new file mode 100644 index 000000000..d5c7486f1 --- /dev/null +++ b/src/context/service/database/SliceModel.py @@ -0,0 +1,85 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 functools, logging, operator +from enum import Enum +from typing import Dict, List +from common.orm.fields.EnumeratedField import EnumeratedField +from common.orm.fields.ForeignKeyField import ForeignKeyField +from common.orm.fields.PrimaryKeyField import PrimaryKeyField +from common.orm.fields.StringField import StringField +from common.orm.model.Model import Model +from common.orm.HighLevel import get_related_objects +from context.proto.context_pb2 import SliceStatusEnum +from .ConstraintModel import ConstraintsModel +from .ContextModel import ContextModel +from .Tools import grpc_to_enum + +LOGGER = logging.getLogger(__name__) + +class ORM_SliceStatusEnum(Enum): + UNDEFINED = SliceStatusEnum.SLICESTATUS_UNDEFINED + PLANNED = SliceStatusEnum.SLICESTATUS_PLANNED + INIT = SliceStatusEnum.SLICESTATUS_INIT + ACTIVE = SliceStatusEnum.SLICESTATUS_ACTIVE + DEINIT = SliceStatusEnum.SLICESTATUS_DEINIT + +grpc_to_enum__slice_status = functools.partial( + grpc_to_enum, SliceStatusEnum, ORM_SliceStatusEnum) + +class SliceModel(Model): + pk = PrimaryKeyField() + context_fk = ForeignKeyField(ContextModel) + slice_uuid = StringField(required=True, allow_empty=False) + slice_constraints_fk = ForeignKeyField(ConstraintsModel) + slice_status = EnumeratedField(ORM_SliceStatusEnum, required=True) + + def dump_id(self) -> Dict: + context_id = ContextModel(self.database, self.context_fk).dump_id() + return { + 'context_id': context_id, + 'slice_uuid': {'uuid': self.slice_uuid}, + } + + def dump_endpoint_ids(self) -> List[Dict]: + from .RelationModels import SliceEndPointModel # pylint: disable=import-outside-toplevel + db_endpoints = get_related_objects(self, SliceEndPointModel, 'endpoint_fk') + return [db_endpoint.dump_id() for db_endpoint in sorted(db_endpoints, key=operator.attrgetter('pk'))] + + def dump_constraints(self) -> List[Dict]: + return ConstraintsModel(self.database, self.slice_constraints_fk).dump() + + def dump_service_ids(self) -> List[Dict]: + from .RelationModels import SliceServiceModel # pylint: disable=import-outside-toplevel + db_services = get_related_objects(self, SliceServiceModel, 'service_fk') + return [db_service.dump_id() for db_service in sorted(db_services, key=operator.attrgetter('pk'))] + + def dump_subslice_ids(self) -> List[Dict]: + from .RelationModels import SliceSubSliceModel # pylint: disable=import-outside-toplevel + db_subslices = get_related_objects(self, SliceSubSliceModel, 'sub_slice_fk') + return [db_subslice.dump_id() for db_subslice in sorted(db_subslices, key=operator.attrgetter('pk'))] + + def dump( # pylint: disable=arguments-differ + self, include_endpoint_ids=True, include_constraints=True, include_service_ids=True, + include_subslice_ids=True + ) -> Dict: + result = { + 'slice_id': self.dump_id(), + 'slice_status': {'slice_status': self.slice_status.value}, + } + if include_endpoint_ids: result['slice_endpoint_ids'] = self.dump_endpoint_ids() + if include_constraints: result['slice_constraints'] = self.dump_constraints() + if include_service_ids: result['slice_service_ids'] = self.dump_service_ids() + if include_subslice_ids: result['sub_subslice_ids'] = self.dump_subslice_ids() + return result diff --git a/src/context/service/grpc_server/Constants.py b/src/context/service/grpc_server/Constants.py index b9676397c..9d7c886c7 100644 --- a/src/context/service/grpc_server/Constants.py +++ b/src/context/service/grpc_server/Constants.py @@ -12,13 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. +TOPIC_CONNECTION = 'connection' TOPIC_CONTEXT = 'context' TOPIC_TOPOLOGY = 'topology' TOPIC_DEVICE = 'device' TOPIC_LINK = 'link' TOPIC_SERVICE = 'service' -TOPIC_CONNECTION = 'connection' +TOPIC_SLICE = 'slice' -TOPICS = {TOPIC_CONTEXT, TOPIC_TOPOLOGY, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_CONNECTION} +TOPICS = {TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_TOPOLOGY, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE} CONSUME_TIMEOUT = 0.5 # seconds diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py index 8e4059215..223c0466a 100644 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py @@ -24,8 +24,8 @@ from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException from context.proto.context_pb2 import ( Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, Context, ContextEvent, ContextId, ContextIdList, ContextList, Device, DeviceEvent, DeviceId, DeviceIdList, DeviceList, Empty, EventTypeEnum, Link, - LinkEvent, LinkId, LinkIdList, LinkList, Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList, Topology, - TopologyEvent, TopologyId, TopologyIdList, TopologyList) + LinkEvent, LinkId, LinkIdList, LinkList, Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList, Slice, SliceEvent, + SliceId, SliceIdList, SliceList, Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList) from context.proto.context_pb2_grpc import ContextServiceServicer from context.service.database.ConfigModel import ConfigModel, ConfigRuleModel, grpc_config_rules_to_raw, update_config from context.service.database.ConnectionModel import ConnectionModel, PathHopModel, PathModel, set_path @@ -37,12 +37,13 @@ from context.service.database.EndPointModel import EndPointModel, KpiSampleTypeM from context.service.database.Events import notify_event from context.service.database.LinkModel import LinkModel from context.service.database.RelationModels import ( - ConnectionSubServiceModel, LinkEndPointModel, ServiceEndPointModel, TopologyDeviceModel, TopologyLinkModel) + ConnectionSubServiceModel, LinkEndPointModel, ServiceEndPointModel, SliceEndPointModel, SliceServiceModel, SliceSubSliceModel, TopologyDeviceModel, TopologyLinkModel) from context.service.database.ServiceModel import ( ServiceModel, grpc_to_enum__service_status, grpc_to_enum__service_type) +from context.service.database.SliceModel import SliceModel, grpc_to_enum__slice_status from context.service.database.TopologyModel import TopologyModel from .Constants import ( - CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_TOPOLOGY) + CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE, TOPIC_TOPOLOGY) LOGGER = logging.getLogger(__name__) @@ -54,6 +55,7 @@ METHOD_NAMES = [ 'ListDeviceIds', 'ListDevices', 'GetDevice', 'SetDevice', 'RemoveDevice', 'GetDeviceEvents', 'ListLinkIds', 'ListLinks', 'GetLink', 'SetLink', 'RemoveLink', 'GetLinkEvents', 'ListServiceIds', 'ListServices', 'GetService', 'SetService', 'RemoveService', 'GetServiceEvents', + 'ListSliceIds', 'ListSlices', 'GetSlice', 'SetSlice', 'RemoveSlice', 'GetSliceEvents', ] METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) @@ -183,7 +185,8 @@ class ContextServiceServicerImpl(ContextServiceServicer): topology_uuid = request.topology_id.topology_uuid.uuid str_topology_key = key_to_str([context_uuid, topology_uuid]) result : Tuple[TopologyModel, bool] = update_or_create_object( - self.database, TopologyModel, str_topology_key, {'context_fk': db_context, 'topology_uuid': topology_uuid}) + self.database, TopologyModel, str_topology_key, { + 'context_fk': db_context, 'topology_uuid': topology_uuid}) db_topology,updated = result for device_id in request.device_ids: @@ -403,7 +406,8 @@ class ContextServiceServicerImpl(ContextServiceServicer): str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) db_topology : TopologyModel = get_object(self.database, TopologyModel, str_topology_key) str_topology_device_key = key_to_str([str_topology_key, endpoint_device_uuid], separator='--') - get_object(self.database, TopologyDeviceModel, str_topology_device_key) # check device is in topology + # check device is in topology + get_object(self.database, TopologyDeviceModel, str_topology_device_key) str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) @@ -491,7 +495,8 @@ class ContextServiceServicerImpl(ContextServiceServicer): raise InvalidArgumentException( 'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), endpoint_topology_context_uuid, - ['should be == {:s}({:s})'.format('request.service_id.context_id.context_uuid.uuid', context_uuid)]) + ['should be == {:s}({:s})'.format( + 'request.service_id.context_id.context_uuid.uuid', context_uuid)]) service_uuid = request.service_id.service_uuid.uuid str_service_key = key_to_str([context_uuid, service_uuid]) @@ -574,6 +579,148 @@ class ContextServiceServicerImpl(ContextServiceServicer): yield ServiceEvent(**json.loads(message.content)) + # ----- Slice ---------------------------------------------------------------------------------------------------- + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def ListSliceIds(self, request: ContextId, context : grpc.ServicerContext) -> SliceIdList: + with self.lock: + db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid) + db_slices : Set[SliceModel] = get_related_objects(db_context, SliceModel) + db_slices = sorted(db_slices, key=operator.attrgetter('pk')) + return SliceIdList(slice_ids=[db_slice.dump_id() for db_slice in db_slices]) + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def ListSlices(self, request: ContextId, context : grpc.ServicerContext) -> SliceList: + with self.lock: + db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid) + db_slices : Set[SliceModel] = get_related_objects(db_context, SliceModel) + db_slices = sorted(db_slices, key=operator.attrgetter('pk')) + return SliceList(slices=[db_slice.dump() for db_slice in db_slices]) + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def GetSlice(self, request: SliceId, context : grpc.ServicerContext) -> Slice: + with self.lock: + str_key = key_to_str([request.context_id.context_uuid.uuid, request.slice_uuid.uuid]) + db_slice : SliceModel = get_object(self.database, SliceModel, str_key) + return Slice(**db_slice.dump( + include_endpoint_ids=True, include_constraints=True, include_service_ids=True, + include_subslice_ids=True)) + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def SetSlice(self, request: Slice, context : grpc.ServicerContext) -> SliceId: + with self.lock: + context_uuid = request.service_id.context_id.context_uuid.uuid + db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) + + for i,endpoint_id in enumerate(request.slice_endpoint_ids): + endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid + if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: + raise InvalidArgumentException( + 'request.slice_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), + endpoint_topology_context_uuid, + ['should be == {:s}({:s})'.format( + 'request.slice_id.context_id.context_uuid.uuid', context_uuid)]) + + slice_uuid = request.slice_id.slice_uuid.uuid + str_slice_key = key_to_str([context_uuid, slice_uuid]) + + constraints_result = set_constraints( + self.database, str_slice_key, 'constraints', request.slice_constraints) + db_constraints = constraints_result[0][0] + + result : Tuple[SliceModel, bool] = update_or_create_object(self.database, SliceModel, str_slice_key, { + 'context_fk' : db_context, + 'slice_uuid' : slice_uuid, + 'slice_constraints_fk': db_constraints, + 'slice_status' : grpc_to_enum__slice_status(request.slice_status.slice_status), + }) + db_slice, updated = result + + for i,endpoint_id in enumerate(request.slice_endpoint_ids): + endpoint_uuid = endpoint_id.endpoint_uuid.uuid + endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid + endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid + endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid + + str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) + if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: + str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) + str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') + + db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) + + str_slice_endpoint_key = key_to_str([slice_uuid, str_endpoint_key], separator='--') + result : Tuple[SliceEndPointModel, bool] = get_or_create_object( + self.database, SliceEndPointModel, str_slice_endpoint_key, { + 'slice_fk': db_slice, 'endpoint_fk': db_endpoint}) + #db_slice_endpoint, slice_endpoint_created = result + + for i,service_id in enumerate(request.slice_service_ids): + service_uuid = service_id.service_uuid.uuid + service_context_uuid = service_id.context_id.context_uuid.uuid + str_service_key = key_to_str([service_context_uuid, service_uuid]) + db_service : ServiceModel = get_object(self.database, ServiceModel, str_service_key) + + str_slice_service_key = key_to_str([str_slice_key, str_service_key], separator='--') + result : Tuple[SliceServiceModel, bool] = get_or_create_object( + self.database, SliceServiceModel, str_slice_service_key, { + 'slice_fk': db_slice, 'service_fk': db_service}) + #db_slice_service, slice_service_created = result + + for i,subslice_id in enumerate(request.slice_subslice_ids): + subslice_uuid = subslice_id.slice_uuid.uuid + subslice_context_uuid = subslice_id.context_id.context_uuid.uuid + str_subslice_key = key_to_str([subslice_context_uuid, subslice_uuid]) + db_subslice : SliceModel = get_object(self.database, SliceModel, str_subslice_key) + + str_slice_subslice_key = key_to_str([str_slice_key, str_subslice_key], separator='--') + result : Tuple[SliceSubSliceModel, bool] = get_or_create_object( + self.database, SliceSubSliceModel, str_slice_subslice_key, { + 'slice_fk': db_slice, 'sub_slice_fk': db_subslice}) + #db_slice_subslice, slice_subslice_created = result + + event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE + dict_slice_id = db_slice.dump_id() + notify_event(self.messagebroker, TOPIC_SLICE, event_type, {'slice_id': dict_slice_id}) + return SliceId(**dict_slice_id) + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def RemoveSlice(self, request: SliceId, context : grpc.ServicerContext) -> Empty: + with self.lock: + context_uuid = request.context_id.context_uuid.uuid + slice_uuid = request.slice_uuid.uuid + db_slice = SliceModel(self.database, key_to_str([context_uuid, slice_uuid]), auto_load=False) + found = db_slice.load() + if not found: return Empty() + + dict_slice_id = db_slice.dump_id() + + for db_slice_endpoint_pk,_ in db_slice.references(SliceEndPointModel): + SliceEndPointModel(self.database, db_slice_endpoint_pk).delete() + + db_constraints = ConstraintsModel(self.database, db_slice.slice_constraints_fk) + for db_constraint_pk,_ in db_constraints.references(ConstraintModel): + ConstraintModel(self.database, db_constraint_pk).delete() + + for db_slice_service_pk,_ in db_slice.references(SliceServiceModel): + SliceServiceModel(self.database, db_slice_service_pk).delete() + + for db_slice_subslice_pk,_ in db_slice.references(SliceSubSliceModel): + SliceSubSliceModel(self.database, db_slice_subslice_pk).delete() + + db_slice.delete() + db_constraints.delete() + + event_type = EventTypeEnum.EVENTTYPE_REMOVE + notify_event(self.messagebroker, TOPIC_SLICE, event_type, {'slice_id': dict_slice_id}) + return Empty() + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def GetSliceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[SliceEvent]: + for message in self.messagebroker.consume({TOPIC_SLICE}, consume_timeout=CONSUME_TIMEOUT): + yield SliceEvent(**json.loads(message.content)) + + # ----- Connection ------------------------------------------------------------------------------------------------- @safe_and_metered_rpc_method(METRICS, LOGGER) -- GitLab From f03b964492f575994ec02e66c110a6317085da12 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Fri, 11 Mar 2022 19:11:36 +0100 Subject: [PATCH 017/353] Proto: - Added RequestSlice to Interdomain - Separated Create and Update in Slice --- proto/interdomain.proto | 1 + proto/slice.proto | 5 +- src/interdomain/proto/interdomain_pb2.py | 22 +++++--- src/interdomain/proto/interdomain_pb2_grpc.py | 33 ++++++++++++ src/interdomain/proto/slice_pb2.py | 24 ++++++--- src/slice/proto/slice_pb2.py | 24 ++++++--- src/slice/proto/slice_pb2_grpc.py | 53 +++++++++++++++---- 7 files changed, 130 insertions(+), 32 deletions(-) diff --git a/proto/interdomain.proto b/proto/interdomain.proto index 9b02c2918..735d4c1cd 100644 --- a/proto/interdomain.proto +++ b/proto/interdomain.proto @@ -19,6 +19,7 @@ import "context.proto"; service InterdomainService { rpc Authenticate (context.TeraFlowController) returns (context.AuthenticationResult) {} + rpc RequestSlice (context.Slice ) returns (context.SliceId ) {} rpc LookUpSlice (context.Slice ) returns (context.SliceId ) {} rpc OrderSliceFromCatalog (context.Slice ) returns (context.Slice ) {} rpc CreateSliceAndAddToCatalog(context.Slice ) returns (context.Slice ) {} diff --git a/proto/slice.proto b/proto/slice.proto index ef6567d78..9c518c9da 100644 --- a/proto/slice.proto +++ b/proto/slice.proto @@ -18,6 +18,7 @@ package slice; import "context.proto"; service SliceService { - rpc CreateUpdateSlice (context.Slice) returns (context.SliceId) {} - rpc DeleteSlice (context.Slice) returns (context.Empty ) {} + rpc CreateSlice(context.Slice ) returns (context.SliceId) {} + rpc UpdateSlice(context.Slice ) returns (context.SliceId) {} + rpc DeleteSlice(context.SliceId) returns (context.Empty ) {} } diff --git a/src/interdomain/proto/interdomain_pb2.py b/src/interdomain/proto/interdomain_pb2.py index 9745bafeb..3f2dff89f 100644 --- a/src/interdomain/proto/interdomain_pb2.py +++ b/src/interdomain/proto/interdomain_pb2.py @@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x11interdomain.proto\x12\x0binterdomain\x1a\rcontext.proto2\x90\x02\n\x12InterdomainService\x12L\n\x0c\x41uthenticate\x12\x1b.context.TeraFlowController\x1a\x1d.context.AuthenticationResult\"\x00\x12\x31\n\x0bLookUpSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x39\n\x15OrderSliceFromCatalog\x12\x0e.context.Slice\x1a\x0e.context.Slice\"\x00\x12>\n\x1a\x43reateSliceAndAddToCatalog\x12\x0e.context.Slice\x1a\x0e.context.Slice\"\x00\x62\x06proto3' + serialized_pb=b'\n\x11interdomain.proto\x12\x0binterdomain\x1a\rcontext.proto2\xc4\x02\n\x12InterdomainService\x12\x32\n\x0cRequestSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12L\n\x0c\x41uthenticate\x12\x1b.context.TeraFlowController\x1a\x1d.context.AuthenticationResult\"\x00\x12\x31\n\x0bLookUpSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x39\n\x15OrderSliceFromCatalog\x12\x0e.context.Slice\x1a\x0e.context.Slice\"\x00\x12>\n\x1a\x43reateSliceAndAddToCatalog\x12\x0e.context.Slice\x1a\x0e.context.Slice\"\x00\x62\x06proto3' , dependencies=[context__pb2.DESCRIPTOR,]) @@ -38,12 +38,22 @@ _INTERDOMAINSERVICE = _descriptor.ServiceDescriptor( serialized_options=None, create_key=_descriptor._internal_create_key, serialized_start=50, - serialized_end=322, + serialized_end=374, methods=[ + _descriptor.MethodDescriptor( + name='RequestSlice', + full_name='interdomain.InterdomainService.RequestSlice', + index=0, + containing_service=None, + input_type=context__pb2._SLICE, + output_type=context__pb2._SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), _descriptor.MethodDescriptor( name='Authenticate', full_name='interdomain.InterdomainService.Authenticate', - index=0, + index=1, containing_service=None, input_type=context__pb2._TERAFLOWCONTROLLER, output_type=context__pb2._AUTHENTICATIONRESULT, @@ -53,7 +63,7 @@ _INTERDOMAINSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='LookUpSlice', full_name='interdomain.InterdomainService.LookUpSlice', - index=1, + index=2, containing_service=None, input_type=context__pb2._SLICE, output_type=context__pb2._SLICEID, @@ -63,7 +73,7 @@ _INTERDOMAINSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='OrderSliceFromCatalog', full_name='interdomain.InterdomainService.OrderSliceFromCatalog', - index=2, + index=3, containing_service=None, input_type=context__pb2._SLICE, output_type=context__pb2._SLICE, @@ -73,7 +83,7 @@ _INTERDOMAINSERVICE = _descriptor.ServiceDescriptor( _descriptor.MethodDescriptor( name='CreateSliceAndAddToCatalog', full_name='interdomain.InterdomainService.CreateSliceAndAddToCatalog', - index=3, + index=4, containing_service=None, input_type=context__pb2._SLICE, output_type=context__pb2._SLICE, diff --git a/src/interdomain/proto/interdomain_pb2_grpc.py b/src/interdomain/proto/interdomain_pb2_grpc.py index 60f142859..0973228d0 100644 --- a/src/interdomain/proto/interdomain_pb2_grpc.py +++ b/src/interdomain/proto/interdomain_pb2_grpc.py @@ -14,6 +14,11 @@ class InterdomainServiceStub(object): Args: channel: A grpc.Channel. """ + self.RequestSlice = channel.unary_unary( + '/interdomain.InterdomainService/RequestSlice', + request_serializer=context__pb2.Slice.SerializeToString, + response_deserializer=context__pb2.SliceId.FromString, + ) self.Authenticate = channel.unary_unary( '/interdomain.InterdomainService/Authenticate', request_serializer=context__pb2.TeraFlowController.SerializeToString, @@ -39,6 +44,12 @@ class InterdomainServiceStub(object): class InterdomainServiceServicer(object): """Missing associated documentation comment in .proto file.""" + def RequestSlice(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 Authenticate(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -66,6 +77,11 @@ class InterdomainServiceServicer(object): def add_InterdomainServiceServicer_to_server(servicer, server): rpc_method_handlers = { + 'RequestSlice': grpc.unary_unary_rpc_method_handler( + servicer.RequestSlice, + request_deserializer=context__pb2.Slice.FromString, + response_serializer=context__pb2.SliceId.SerializeToString, + ), 'Authenticate': grpc.unary_unary_rpc_method_handler( servicer.Authenticate, request_deserializer=context__pb2.TeraFlowController.FromString, @@ -96,6 +112,23 @@ def add_InterdomainServiceServicer_to_server(servicer, server): class InterdomainService(object): """Missing associated documentation comment in .proto file.""" + @staticmethod + def RequestSlice(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, '/interdomain.InterdomainService/RequestSlice', + context__pb2.Slice.SerializeToString, + context__pb2.SliceId.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + @staticmethod def Authenticate(request, target, diff --git a/src/interdomain/proto/slice_pb2.py b/src/interdomain/proto/slice_pb2.py index ac8b165db..1e2a5f31c 100644 --- a/src/interdomain/proto/slice_pb2.py +++ b/src/interdomain/proto/slice_pb2.py @@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x0bslice.proto\x12\x05slice\x1a\rcontext.proto2x\n\x0cSliceService\x12\x37\n\x11\x43reateUpdateSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12/\n\x0b\x44\x65leteSlice\x12\x0e.context.Slice\x1a\x0e.context.Empty\"\x00\x62\x06proto3' + serialized_pb=b'\n\x0bslice.proto\x12\x05slice\x1a\rcontext.proto2\xa7\x01\n\x0cSliceService\x12\x31\n\x0b\x43reateSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bUpdateSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0b\x44\x65leteSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x62\x06proto3' , dependencies=[context__pb2.DESCRIPTOR,]) @@ -37,12 +37,12 @@ _SLICESERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=37, - serialized_end=157, + serialized_start=38, + serialized_end=205, methods=[ _descriptor.MethodDescriptor( - name='CreateUpdateSlice', - full_name='slice.SliceService.CreateUpdateSlice', + name='CreateSlice', + full_name='slice.SliceService.CreateSlice', index=0, containing_service=None, input_type=context__pb2._SLICE, @@ -51,11 +51,21 @@ _SLICESERVICE = _descriptor.ServiceDescriptor( create_key=_descriptor._internal_create_key, ), _descriptor.MethodDescriptor( - name='DeleteSlice', - full_name='slice.SliceService.DeleteSlice', + name='UpdateSlice', + full_name='slice.SliceService.UpdateSlice', index=1, containing_service=None, input_type=context__pb2._SLICE, + output_type=context__pb2._SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='DeleteSlice', + full_name='slice.SliceService.DeleteSlice', + index=2, + containing_service=None, + input_type=context__pb2._SLICEID, output_type=context__pb2._EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, diff --git a/src/slice/proto/slice_pb2.py b/src/slice/proto/slice_pb2.py index ac8b165db..1e2a5f31c 100644 --- a/src/slice/proto/slice_pb2.py +++ b/src/slice/proto/slice_pb2.py @@ -20,7 +20,7 @@ DESCRIPTOR = _descriptor.FileDescriptor( syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x0bslice.proto\x12\x05slice\x1a\rcontext.proto2x\n\x0cSliceService\x12\x37\n\x11\x43reateUpdateSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12/\n\x0b\x44\x65leteSlice\x12\x0e.context.Slice\x1a\x0e.context.Empty\"\x00\x62\x06proto3' + serialized_pb=b'\n\x0bslice.proto\x12\x05slice\x1a\rcontext.proto2\xa7\x01\n\x0cSliceService\x12\x31\n\x0b\x43reateSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bUpdateSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0b\x44\x65leteSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x62\x06proto3' , dependencies=[context__pb2.DESCRIPTOR,]) @@ -37,12 +37,12 @@ _SLICESERVICE = _descriptor.ServiceDescriptor( index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=37, - serialized_end=157, + serialized_start=38, + serialized_end=205, methods=[ _descriptor.MethodDescriptor( - name='CreateUpdateSlice', - full_name='slice.SliceService.CreateUpdateSlice', + name='CreateSlice', + full_name='slice.SliceService.CreateSlice', index=0, containing_service=None, input_type=context__pb2._SLICE, @@ -51,11 +51,21 @@ _SLICESERVICE = _descriptor.ServiceDescriptor( create_key=_descriptor._internal_create_key, ), _descriptor.MethodDescriptor( - name='DeleteSlice', - full_name='slice.SliceService.DeleteSlice', + name='UpdateSlice', + full_name='slice.SliceService.UpdateSlice', index=1, containing_service=None, input_type=context__pb2._SLICE, + output_type=context__pb2._SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='DeleteSlice', + full_name='slice.SliceService.DeleteSlice', + index=2, + containing_service=None, + input_type=context__pb2._SLICEID, output_type=context__pb2._EMPTY, serialized_options=None, create_key=_descriptor._internal_create_key, diff --git a/src/slice/proto/slice_pb2_grpc.py b/src/slice/proto/slice_pb2_grpc.py index bcd736689..abc2d884f 100644 --- a/src/slice/proto/slice_pb2_grpc.py +++ b/src/slice/proto/slice_pb2_grpc.py @@ -14,14 +14,19 @@ class SliceServiceStub(object): Args: channel: A grpc.Channel. """ - self.CreateUpdateSlice = channel.unary_unary( - '/slice.SliceService/CreateUpdateSlice', + self.CreateSlice = channel.unary_unary( + '/slice.SliceService/CreateSlice', + request_serializer=context__pb2.Slice.SerializeToString, + response_deserializer=context__pb2.SliceId.FromString, + ) + self.UpdateSlice = channel.unary_unary( + '/slice.SliceService/UpdateSlice', request_serializer=context__pb2.Slice.SerializeToString, response_deserializer=context__pb2.SliceId.FromString, ) self.DeleteSlice = channel.unary_unary( '/slice.SliceService/DeleteSlice', - request_serializer=context__pb2.Slice.SerializeToString, + request_serializer=context__pb2.SliceId.SerializeToString, response_deserializer=context__pb2.Empty.FromString, ) @@ -29,7 +34,13 @@ class SliceServiceStub(object): class SliceServiceServicer(object): """Missing associated documentation comment in .proto file.""" - def CreateUpdateSlice(self, request, context): + def CreateSlice(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 UpdateSlice(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -44,14 +55,19 @@ class SliceServiceServicer(object): def add_SliceServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'CreateUpdateSlice': grpc.unary_unary_rpc_method_handler( - servicer.CreateUpdateSlice, + 'CreateSlice': grpc.unary_unary_rpc_method_handler( + servicer.CreateSlice, + request_deserializer=context__pb2.Slice.FromString, + response_serializer=context__pb2.SliceId.SerializeToString, + ), + 'UpdateSlice': grpc.unary_unary_rpc_method_handler( + servicer.UpdateSlice, request_deserializer=context__pb2.Slice.FromString, response_serializer=context__pb2.SliceId.SerializeToString, ), 'DeleteSlice': grpc.unary_unary_rpc_method_handler( servicer.DeleteSlice, - request_deserializer=context__pb2.Slice.FromString, + request_deserializer=context__pb2.SliceId.FromString, response_serializer=context__pb2.Empty.SerializeToString, ), } @@ -65,7 +81,7 @@ class SliceService(object): """Missing associated documentation comment in .proto file.""" @staticmethod - def CreateUpdateSlice(request, + def CreateSlice(request, target, options=(), channel_credentials=None, @@ -75,7 +91,24 @@ class SliceService(object): wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/slice.SliceService/CreateUpdateSlice', + return grpc.experimental.unary_unary(request, target, '/slice.SliceService/CreateSlice', + context__pb2.Slice.SerializeToString, + context__pb2.SliceId.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def UpdateSlice(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/UpdateSlice', context__pb2.Slice.SerializeToString, context__pb2.SliceId.FromString, options, channel_credentials, @@ -93,7 +126,7 @@ class SliceService(object): timeout=None, metadata=None): return grpc.experimental.unary_unary(request, target, '/slice.SliceService/DeleteSlice', - context__pb2.Slice.SerializeToString, + context__pb2.SliceId.SerializeToString, context__pb2.Empty.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) -- GitLab From a2003459ccd4d93ca79bddcb577b24bc29490d1b Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Fri, 11 Mar 2022 19:15:38 +0100 Subject: [PATCH 018/353] Common: - improved loggers in decorators for client and server methods - corrected error in delay_exponential for retry decorator --- src/common/rpc_method_wrapper/Decorator.py | 5 +++-- src/common/tools/client/RetryDecorator.py | 2 +- src/common/tools/grpc/Tools.py | 5 +++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/common/rpc_method_wrapper/Decorator.py b/src/common/rpc_method_wrapper/Decorator.py index 212481b73..31dc4b82b 100644 --- a/src/common/rpc_method_wrapper/Decorator.py +++ b/src/common/rpc_method_wrapper/Decorator.py @@ -17,6 +17,7 @@ from enum import Enum from typing import Dict, List from prometheus_client import Counter, Histogram from prometheus_client.metrics import MetricWrapperBase +from common.tools.grpc.Tools import grpc_message_to_json_string from .ServiceExceptions import ServiceException class RequestConditionEnum(Enum): @@ -62,9 +63,9 @@ def safe_and_metered_rpc_method(metrics : Dict[str, MetricWrapperBase], logger : def inner_wrapper(self, request, grpc_context : grpc.ServicerContext): COUNTER_STARTED.inc() try: - logger.debug('{:s} request: {:s}'.format(function_name, str(request))) + logger.debug('{:s} request: {:s}'.format(function_name, grpc_message_to_json_string(request))) reply = func(self, request, grpc_context) - logger.debug('{:s} reply: {:s}'.format(function_name, str(reply))) + logger.debug('{:s} reply: {:s}'.format(function_name, grpc_message_to_json_string(reply))) COUNTER_COMPLETED.inc() return reply except ServiceException as e: # pragma: no cover (ServiceException not thrown) diff --git a/src/common/tools/client/RetryDecorator.py b/src/common/tools/client/RetryDecorator.py index d7bcdb42d..9a1c0d69f 100644 --- a/src/common/tools/client/RetryDecorator.py +++ b/src/common/tools/client/RetryDecorator.py @@ -57,7 +57,7 @@ def delay_linear(initial=0, increment=0, maximum=None): def delay_exponential(initial=1, increment=1, maximum=None): def compute(num_try): - delay = initial * pow((num_try - 1), increment) + delay = initial * pow(increment, (num_try - 1)) if maximum is not None: delay = max(delay, maximum) return delay return compute diff --git a/src/common/tools/grpc/Tools.py b/src/common/tools/grpc/Tools.py index 7c6a74348..f0c72a36f 100644 --- a/src/common/tools/grpc/Tools.py +++ b/src/common/tools/grpc/Tools.py @@ -16,8 +16,9 @@ import json from google.protobuf.json_format import MessageToDict def grpc_message_to_json( - message, including_default_value_fields=True, preserving_proto_field_name=True, use_integers_for_enums=False): - + message, including_default_value_fields=True, preserving_proto_field_name=True, use_integers_for_enums=False + ): + if not hasattr(message, 'DESCRIPTOR'): return json.dumps(str(message), sort_keys=True) # not a gRPC message return MessageToDict( message, including_default_value_fields=including_default_value_fields, preserving_proto_field_name=preserving_proto_field_name, use_integers_for_enums=use_integers_for_enums) -- GitLab From c6699878f15def6aac2a81da779aadcbf6ecb0f0 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Fri, 11 Mar 2022 19:24:30 +0100 Subject: [PATCH 019/353] Integrated basic slicing skeleton. Compute: - added support for Slice creation when VPLS service is requested instead of VPWS Context: - minor improvements in client class logging - added slice events to EventsCollector - minor bug fixes in SliceModel Interdomain: - Implemented basic workflow Monitoring: - fixed requirements versions Service: - minor improvements in client and server logging Slice: - Implemented basic workflow OECC/PSC'22 functional test: - updated test objects - updated deployment script - updated exposed services --- src/compute/Dockerfile | 1 + .../nbi_plugins/ietf_l2vpn/Constants.py | 26 ++ .../nbi_plugins/ietf_l2vpn/L2VPN_Service.py | 37 +- .../nbi_plugins/ietf_l2vpn/L2VPN_Services.py | 36 +- .../ietf_l2vpn/L2VPN_SiteNetworkAccesses.py | 67 ++-- .../ietf_l2vpn/schemas/vpn_service.py | 2 +- .../ietf_l2vpn/tools/ContextMethods.py | 39 +++ .../tests/mock_osm/WimconnectorIETFL2VPN.py | 315 +++++++++--------- src/context/client/ContextClient.py | 191 +++++++---- src/context/client/EventsCollector.py | 5 + src/context/service/database/SliceModel.py | 2 +- .../grpc_server/ContextServiceServicerImpl.py | 2 +- src/interdomain/Config.py | 5 +- src/interdomain/client/InterdomainClient.py | 33 +- src/interdomain/requirements.in | 1 + src/interdomain/service/InterdomainService.py | 14 +- .../service/InterdomainServiceServicerImpl.py | 125 ++++++- .../service/RemoteDomainClients.py | 42 +++ src/interdomain/service/__main__.py | 41 ++- src/monitoring/requirements.in | 29 +- src/service/client/ServiceClient.py | 13 +- .../service/ServiceServiceServicerImpl.py | 2 + src/slice/Config.py | 12 +- src/slice/Dockerfile | 3 + src/slice/_docs/old_conflicting_code.txt | 176 ++++++++++ src/slice/client/SliceClient.py | 27 +- src/slice/requirements.in | 1 + src/slice/service/SliceService.py | 13 +- src/slice/service/SliceServiceServicerImpl.py | 113 ++++++- src/slice/service/__main__.py | 65 ++-- src/tests/oeccpsc22/deploy_in_kubernetes.sh | 144 +++++++- src/tests/oeccpsc22/dump_logs.sh | 19 ++ .../oeccpsc22/expose_services_teraflow_1.yaml | 5 + .../oeccpsc22/expose_services_teraflow_2.yaml | 7 +- src/tests/oeccpsc22/tests/Objects_Domain_1.py | 17 +- src/tests/oeccpsc22/tests/Objects_Domain_2.py | 17 +- 36 files changed, 1234 insertions(+), 413 deletions(-) create mode 100644 src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/ContextMethods.py create mode 100644 src/interdomain/service/RemoteDomainClients.py create mode 100644 src/slice/_docs/old_conflicting_code.txt create mode 100755 src/tests/oeccpsc22/dump_logs.sh diff --git a/src/compute/Dockerfile b/src/compute/Dockerfile index 6d3cafda9..bb10332d1 100644 --- a/src/compute/Dockerfile +++ b/src/compute/Dockerfile @@ -46,6 +46,7 @@ COPY common/. common COPY compute/. compute COPY context/. context COPY service/. service +COPY slice/. slice # Start compute service ENTRYPOINT ["python", "-m", "compute.service"] diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py index 9420517e1..b7f377254 100644 --- a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py +++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/Constants.py @@ -25,4 +25,30 @@ BEARER_MAPPINGS = { 'R2-EMU:13/2/1': ('R2-EMU', '13/2/1', '12.12.12.1', '65000:120', 450, '3.4.2.1', 24), 'R3-INF:13/2/1': ('R3-INF', '13/2/1', '20.20.20.1', '65000:200', 500, '3.3.1.1', 24), 'R4-EMU:13/2/1': ('R4-EMU', '13/2/1', '22.22.22.1', '65000:220', 550, '3.4.1.1', 24), + + 'R1@D1:3/1': ('R1@D1', '3/1', '10.0.1.1', '65001:101', 100, '1.1.3.1', 24), + 'R1@D1:3/2': ('R1@D1', '3/2', '10.0.1.1', '65001:101', 100, '1.1.3.2', 24), + 'R1@D1:3/3': ('R1@D1', '3/3', '10.0.1.1', '65001:101', 100, '1.1.3.3', 24), + 'R2@D1:3/1': ('R2@D1', '3/1', '10.0.1.2', '65001:102', 100, '1.2.3.1', 24), + 'R2@D1:3/2': ('R2@D1', '3/2', '10.0.1.2', '65001:102', 100, '1.2.3.2', 24), + 'R2@D1:3/3': ('R2@D1', '3/3', '10.0.1.2', '65001:102', 100, '1.2.3.3', 24), + 'R3@D1:3/1': ('R3@D1', '3/1', '10.0.1.3', '65001:103', 100, '1.3.3.1', 24), + 'R3@D1:3/2': ('R3@D1', '3/2', '10.0.1.3', '65001:103', 100, '1.3.3.2', 24), + 'R3@D1:3/3': ('R3@D1', '3/3', '10.0.1.3', '65001:103', 100, '1.3.3.3', 24), + 'R4@D1:3/1': ('R4@D1', '3/1', '10.0.1.4', '65001:104', 100, '1.4.3.1', 24), + 'R4@D1:3/2': ('R4@D1', '3/2', '10.0.1.4', '65001:104', 100, '1.4.3.2', 24), + 'R4@D1:3/3': ('R4@D1', '3/3', '10.0.1.4', '65001:104', 100, '1.4.3.3', 24), + + 'R1@D2:3/1': ('R1@D2', '3/1', '10.0.2.1', '65002:101', 100, '2.1.3.1', 24), + 'R1@D2:3/2': ('R1@D2', '3/2', '10.0.2.1', '65002:101', 100, '2.1.3.2', 24), + 'R1@D2:3/3': ('R1@D2', '3/3', '10.0.2.1', '65002:101', 100, '2.1.3.3', 24), + 'R2@D2:3/1': ('R2@D2', '3/1', '10.0.2.2', '65002:102', 100, '2.2.3.1', 24), + 'R2@D2:3/2': ('R2@D2', '3/2', '10.0.2.2', '65002:102', 100, '2.2.3.2', 24), + 'R2@D2:3/3': ('R2@D2', '3/3', '10.0.2.2', '65002:102', 100, '2.2.3.3', 24), + 'R3@D2:3/1': ('R3@D2', '3/1', '10.0.2.3', '65002:103', 100, '2.3.3.1', 24), + 'R3@D2:3/2': ('R3@D2', '3/2', '10.0.2.3', '65002:103', 100, '2.3.3.2', 24), + 'R3@D2:3/3': ('R3@D2', '3/3', '10.0.2.3', '65002:103', 100, '2.3.3.3', 24), + 'R4@D2:3/1': ('R4@D2', '3/1', '10.0.2.4', '65002:104', 100, '2.4.3.1', 24), + 'R4@D2:3/2': ('R4@D2', '3/2', '10.0.2.4', '65002:104', 100, '2.4.3.2', 24), + 'R4@D2:3/3': ('R4@D2', '3/3', '10.0.2.4', '65002:104', 100, '2.4.3.3', 24), } diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py index 6a91e6ae1..440b706ab 100644 --- a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py +++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from ctypes import Union import logging from flask import request from flask.json import jsonify @@ -19,10 +20,11 @@ from flask_restful import Resource from common.Constants import DEFAULT_CONTEXT_UUID from common.Settings import get_setting from context.client.ContextClient import ContextClient -from context.proto.context_pb2 import ServiceId +from context.proto.context_pb2 import Service, ServiceId, Slice, SliceStatusEnum from service.client.ServiceClient import ServiceClient from service.proto.context_pb2 import ServiceStatusEnum from .tools.Authentication import HTTP_AUTH +from .tools.ContextMethods import get_service, get_slice from .tools.HttpStatusCodes import HTTP_GATEWAYTIMEOUT, HTTP_NOCONTENT, HTTP_OK, HTTP_SERVERERROR LOGGER = logging.getLogger(__name__) @@ -40,21 +42,30 @@ class L2VPN_Service(Resource): LOGGER.debug('VPN_Id: {:s}'.format(str(vpn_id))) LOGGER.debug('Request: {:s}'.format(str(request))) - # pylint: disable=no-member - service_id_request = ServiceId() - service_id_request.context_id.context_uuid.uuid = DEFAULT_CONTEXT_UUID - service_id_request.service_uuid.uuid = vpn_id + response = jsonify({}) try: - service_reply = self.context_client.GetService(service_id_request) - if service_reply.service_id != service_id_request: # pylint: disable=no-member - raise Exception('Service retrieval failed. Wrong Service Id was returned') - service_ready_status = ServiceStatusEnum.SERVICESTATUS_ACTIVE - service_status = service_reply.service_status.service_status - response = jsonify({}) - response.status_code = HTTP_OK if service_status == service_ready_status else HTTP_GATEWAYTIMEOUT + target = get_service(self.context_client, vpn_id) + if target is not None: + if target.service_id.service_uuid.uuid != vpn_id: # pylint: disable=no-member + raise Exception('Service retrieval failed. Wrong Service Id was returned') + service_ready_status = ServiceStatusEnum.SERVICESTATUS_ACTIVE + service_status = target.service_status.service_status + response.status_code = HTTP_OK if service_status == service_ready_status else HTTP_GATEWAYTIMEOUT + return response + + target = get_slice(self.context_client, vpn_id) + if target is None: + if target.slice_id.slice_uuid.uuid != vpn_id: # pylint: disable=no-member + raise Exception('Slice retrieval failed. Wrong Slice Id was returned') + slice_ready_status = SliceStatusEnum.SLICESTATUS_ACTIVE + slice_status = target.slice_status.slice_status + response.status_code = HTTP_OK if slice_status == slice_ready_status else HTTP_GATEWAYTIMEOUT + return response + + raise Exception('VPN({:s}) not found in database'.format(str(vpn_id))) except Exception as e: # pylint: disable=broad-except - LOGGER.exception('Something went wrong Retrieving Service {:s}'.format(str(request))) + LOGGER.exception('Something went wrong Retrieving VPN({:s})'.format(str(request))) response = jsonify({'error': str(e)}) response.status_code = HTTP_SERVERERROR return response diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Services.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Services.py index 191166a74..6d39cfe2d 100644 --- a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Services.py +++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Services.py @@ -22,6 +22,8 @@ from common.Constants import DEFAULT_CONTEXT_UUID from common.Settings import get_setting from service.client.ServiceClient import ServiceClient from service.proto.context_pb2 import Service, ServiceStatusEnum, ServiceTypeEnum +from slice.client.SliceClient import SliceClient +from slice.proto.context_pb2 import SliceStatusEnum, Slice from .schemas.vpn_service import SCHEMA_VPN_SERVICE from .tools.Authentication import HTTP_AUTH from .tools.HttpStatusCodes import HTTP_CREATED, HTTP_SERVERERROR @@ -34,6 +36,8 @@ class L2VPN_Services(Resource): super().__init__() self.service_client = ServiceClient( get_setting('SERVICESERVICE_SERVICE_HOST'), get_setting('SERVICESERVICE_SERVICE_PORT_GRPC')) + self.slice_client = SliceClient( + get_setting('SLICESERVICE_SERVICE_HOST'), get_setting('SLICESERVICE_SERVICE_PORT_GRPC')) @HTTP_AUTH.login_required def get(self): @@ -48,17 +52,29 @@ class L2VPN_Services(Resource): vpn_services : List[Dict] = request_data['ietf-l2vpn-svc:vpn-service'] for vpn_service in vpn_services: - # pylint: disable=no-member - service_request = Service() - service_request.service_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_UUID - service_request.service_id.service_uuid.uuid = vpn_service['vpn-id'] - service_request.service_type = ServiceTypeEnum.SERVICETYPE_L3NM - service_request.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED - try: - service_reply = self.service_client.CreateService(service_request) - if service_reply != service_request.service_id: # pylint: disable=no-member - raise Exception('Service creation failed. Wrong Service Id was returned') + vpn_service_type = vpn_service['vpn-svc-type'] + if vpn_service_type == 'vpws': + # pylint: disable=no-member + service_request = Service() + service_request.service_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_UUID + service_request.service_id.service_uuid.uuid = vpn_service['vpn-id'] + service_request.service_type = ServiceTypeEnum.SERVICETYPE_L3NM + service_request.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED + + service_reply = self.service_client.CreateService(service_request) + if service_reply != service_request.service_id: # pylint: disable=no-member + raise Exception('Service creation failed. Wrong Service Id was returned') + elif vpn_service_type == 'vpls': + # pylint: disable=no-member + slice_request = Slice() + slice_request.slice_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_UUID + slice_request.slice_id.slice_uuid.uuid = vpn_service['vpn-id'] + slice_request.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_PLANNED + + slice_reply = self.slice_client.CreateSlice(slice_request) + if slice_reply != slice_request.slice_id: # pylint: disable=no-member + raise Exception('Slice creation failed. Wrong Slice Id was returned') response = jsonify({}) response.status_code = HTTP_CREATED diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_SiteNetworkAccesses.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_SiteNetworkAccesses.py index 6811dadac..2c0245b9a 100644 --- a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_SiteNetworkAccesses.py +++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_SiteNetworkAccesses.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from ctypes import Union import json, logging from typing import Dict from flask import request @@ -19,14 +20,15 @@ from flask.json import jsonify from flask.wrappers import Response from flask_restful import Resource from werkzeug.exceptions import UnsupportedMediaType -from common.Constants import DEFAULT_CONTEXT_UUID from common.Settings import get_setting from common.tools.grpc.Tools import grpc_message_to_json_string from context.client.ContextClient import ContextClient -from context.proto.context_pb2 import ConfigActionEnum, Service, ServiceId +from context.proto.context_pb2 import ConfigActionEnum, Service, Slice from service.client.ServiceClient import ServiceClient +from slice.client.SliceClient import SliceClient from .schemas.site_network_access import SCHEMA_SITE_NETWORK_ACCESS from .tools.Authentication import HTTP_AUTH +from .tools.ContextMethods import get_service, get_slice from .tools.HttpStatusCodes import HTTP_NOCONTENT, HTTP_SERVERERROR from .tools.Validator import validate_message from .Constants import BEARER_MAPPINGS, DEFAULT_ADDRESS_FAMILIES, DEFAULT_BGP_AS, DEFAULT_BGP_ROUTE_TARGET, DEFAULT_MTU @@ -44,26 +46,27 @@ def process_site_network_access(context_client : ContextClient, site_network_acc raise Exception(msg.format(str(bearer_reference))) device_uuid,endpoint_uuid,router_id,route_distinguisher,sub_if_index,address_ip,address_prefix = mapping - # pylint: disable=no-member - service_id = ServiceId() - service_id.context_id.context_uuid.uuid = DEFAULT_CONTEXT_UUID - service_id.service_uuid.uuid = vpn_id + target : Union[Service, Slice, None] = None + if target is None: target = get_service(context_client, vpn_id) + if target is None: target = get_slice (context_client, vpn_id) + if target is None: raise Exception('VPN({:s}) not found in database'.format(str(vpn_id))) - service_readonly = context_client.GetService(service_id) - service = Service() - service.CopyFrom(service_readonly) + # pylint: disable=no-member + endpoint_ids = target.service_endpoint_ids if isinstance(target, Service) else target.slice_endpoint_ids - for endpoint_id in service.service_endpoint_ids: # pylint: disable=no-member + for endpoint_id in endpoint_ids: if endpoint_id.device_id.device_uuid.uuid != device_uuid: continue if endpoint_id.endpoint_uuid.uuid != endpoint_uuid: continue break # found, do nothing else: # not found, add it - endpoint_id = service.service_endpoint_ids.add() # pylint: disable=no-member + endpoint_id = endpoint_ids.add() endpoint_id.device_id.device_uuid.uuid = device_uuid endpoint_id.endpoint_uuid.uuid = endpoint_uuid - for config_rule in service.service_config.config_rules: # pylint: disable=no-member + if isinstance(target, Slice): return target + + for config_rule in target.service_config.config_rules: # pylint: disable=no-member if config_rule.resource_key != '/settings': continue json_settings = json.loads(config_rule.resource_value) @@ -95,7 +98,7 @@ def process_site_network_access(context_client : ContextClient, site_network_acc break else: # not found, add it - config_rule = service.service_config.config_rules.add() # pylint: disable=no-member + config_rule = target.service_config.config_rules.add() # pylint: disable=no-member config_rule.action = ConfigActionEnum.CONFIGACTION_SET config_rule.resource_key = '/settings' config_rule.resource_value = json.dumps({ @@ -106,7 +109,7 @@ def process_site_network_access(context_client : ContextClient, site_network_acc }, sort_keys=True) endpoint_settings_key = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid) - for config_rule in service.service_config.config_rules: # pylint: disable=no-member + for config_rule in target.service_config.config_rules: # pylint: disable=no-member if config_rule.resource_key != endpoint_settings_key: continue json_settings = json.loads(config_rule.resource_value) @@ -154,7 +157,7 @@ def process_site_network_access(context_client : ContextClient, site_network_acc break else: # not found, add it - config_rule = service.service_config.config_rules.add() # pylint: disable=no-member + config_rule = target.service_config.config_rules.add() # pylint: disable=no-member config_rule.action = ConfigActionEnum.CONFIGACTION_SET config_rule.resource_key = endpoint_settings_key config_rule.resource_value = json.dumps({ @@ -166,24 +169,34 @@ def process_site_network_access(context_client : ContextClient, site_network_acc 'address_prefix': address_prefix, }, sort_keys=True) - return service + return target def process_list_site_network_access( - context_client : ContextClient, service_client : ServiceClient, request_data : Dict) -> Response: + context_client : ContextClient, service_client : ServiceClient, slice_client : SliceClient, + request_data : Dict + ) -> Response: LOGGER.debug('Request: {:s}'.format(str(request_data))) validate_message(SCHEMA_SITE_NETWORK_ACCESS, request_data) errors = [] for site_network_access in request_data['ietf-l2vpn-svc:site-network-access']: + sna_request = process_site_network_access(context_client, site_network_access) + LOGGER.debug('sna_request = {:s}'.format(grpc_message_to_json_string(sna_request))) try: - service_request = process_site_network_access(context_client, site_network_access) - LOGGER.debug('service_request = {:s}'.format(grpc_message_to_json_string(service_request))) - service_reply = service_client.UpdateService(service_request) - if service_reply != service_request.service_id: # pylint: disable=no-member - raise Exception('Service update failed. Wrong Service Id was returned') + if isinstance(sna_request, Service): + sna_reply = service_client.UpdateService(sna_request) + if sna_reply != sna_request.service_id: # pylint: disable=no-member + raise Exception('Service update failed. Wrong Service Id was returned') + elif isinstance(sna_request, Slice): + sna_reply = slice_client.UpdateSlice(sna_request) + if sna_reply != sna_request.slice_id: # pylint: disable=no-member + raise Exception('Slice update failed. Wrong Slice Id was returned') + else: + raise NotImplementedError('Support for Class({:s}) not implemented'.format(str(type(sna_request)))) except Exception as e: # pylint: disable=broad-except - LOGGER.exception('Something went wrong Updating Service {:s}'.format(str(request))) + msg = 'Something went wrong Updating Service {:s}' + LOGGER.exception(msg.format(grpc_message_to_json_string(sna_request))) errors.append({'error': str(e)}) response = jsonify(errors) @@ -197,15 +210,19 @@ class L2VPN_SiteNetworkAccesses(Resource): get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) self.service_client = ServiceClient( get_setting('SERVICESERVICE_SERVICE_HOST'), get_setting('SERVICESERVICE_SERVICE_PORT_GRPC')) + self.slice_client = SliceClient( + get_setting('SLICESERVICE_SERVICE_HOST'), get_setting('SLICESERVICE_SERVICE_PORT_GRPC')) @HTTP_AUTH.login_required def post(self, site_id : str): if not request.is_json: raise UnsupportedMediaType('JSON payload is required') LOGGER.debug('Site_Id: {:s}'.format(str(site_id))) - return process_list_site_network_access(self.context_client, self.service_client, request.json) + return process_list_site_network_access( + self.context_client, self.service_client, self.slice_client, request.json) @HTTP_AUTH.login_required def put(self, site_id : str): if not request.is_json: raise UnsupportedMediaType('JSON payload is required') LOGGER.debug('Site_Id: {:s}'.format(str(site_id))) - return process_list_site_network_access(self.context_client, self.service_client, request.json) + return process_list_site_network_access( + self.context_client, self.service_client, self.slice_client, request.json) diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/vpn_service.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/vpn_service.py index b224b4073..9dd8eea3d 100644 --- a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/vpn_service.py +++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/schemas/vpn_service.py @@ -36,7 +36,7 @@ SCHEMA_VPN_SERVICE = { 'required': ['vpn-id', 'vpn-svc-type', 'svc-topo', 'customer-name'], 'properties': { 'vpn-id': {'type': 'string', 'pattern': REGEX_UUID}, - 'vpn-svc-type': {'enum': ['vpws']}, + 'vpn-svc-type': {'enum': ['vpws', 'vpls']}, 'svc-topo': {'enum': ['any-to-any']}, 'customer-name': {'const': 'osm'}, }, diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/ContextMethods.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/ContextMethods.py new file mode 100644 index 000000000..79e73a28d --- /dev/null +++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/tools/ContextMethods.py @@ -0,0 +1,39 @@ +import grpc, logging +from typing import Optional +from common.Constants import DEFAULT_CONTEXT_UUID +from context.client.ContextClient import ContextClient +from context.proto.context_pb2 import Service, ServiceId, Slice, SliceId + +LOGGER = logging.getLogger(__name__) + +def get_service( + context_client : ContextClient, service_uuid : str, context_uuid : str = DEFAULT_CONTEXT_UUID + ) -> Optional[Service]: + try: + # pylint: disable=no-member + service_id = ServiceId() + service_id.context_id.context_uuid.uuid = context_uuid + service_id.service_uuid.uuid = service_uuid + service_readonly = context_client.GetService(service_id) + service = Service() + service.CopyFrom(service_readonly) + return service + except grpc.RpcError: + #LOGGER.exception('Unable to get service({:s} / {:s})'.format(str(context_uuid), str(service_uuid))) + return None + +def get_slice( + context_client : ContextClient, slice_uuid : str, context_uuid : str = DEFAULT_CONTEXT_UUID + ) -> Optional[Slice]: + try: + # pylint: disable=no-member + slice_id = SliceId() + slice_id.context_id.context_uuid.uuid = context_uuid + slice_id.slice_uuid.uuid = slice_uuid + slice_readonly = context_client.GetSlice(slice_id) + slice_ = Slice() + slice_.CopyFrom(slice_readonly) + return slice_ + except grpc.RpcError: + #LOGGER.exception('Unable to get slice({:s} / {:s})'.format(str(context_uuid), str(slice_uuid))) + return None diff --git a/src/compute/tests/mock_osm/WimconnectorIETFL2VPN.py b/src/compute/tests/mock_osm/WimconnectorIETFL2VPN.py index d5ce65a1e..b9639e804 100644 --- a/src/compute/tests/mock_osm/WimconnectorIETFL2VPN.py +++ b/src/compute/tests/mock_osm/WimconnectorIETFL2VPN.py @@ -164,178 +164,183 @@ class WimconnectorIETFL2VPN(SdnConnectorBase): Raises: SdnConnectorException: In case of error. """ - if service_type == "ELINE": - if len(connection_points) > 2: - raise SdnConnectorError( - "Connections between more than 2 endpoints are not supported" + SETTINGS = { # min_endpoints, max_endpoints, vpn_service_type + 'ELINE': (2, 2, 'vpws'), # Virtual Private Wire Service + 'ELAN' : (2, None, 'vpls'), # Virtual Private LAN Service + } + settings = SETTINGS.get(service_type) + if settings is None: raise NotImplementedError('Unsupported service_type({:s})'.format(str(service_type))) + min_endpoints, max_endpoints, vpn_service_type = settings + + if max_endpoints is not None and len(connection_points) > max_endpoints: + msg = "Connections between more than {:d} endpoints are not supported for service_type {:s}" + raise SdnConnectorError(msg.format(max_endpoints, service_type)) + + if min_endpoints is not None and len(connection_points) < min_endpoints: + msg = "Connections must be of at least {:d} endpoints for service_type {:s}" + raise SdnConnectorError(msg.format(min_endpoints, service_type)) + + """First step, create the vpn service""" + uuid_l2vpn = str(uuid.uuid4()) + vpn_service = {} + vpn_service["vpn-id"] = uuid_l2vpn + vpn_service["vpn-svc-type"] = vpn_service_type + vpn_service["svc-topo"] = "any-to-any" + vpn_service["customer-name"] = "osm" + vpn_service_list = [] + vpn_service_list.append(vpn_service) + vpn_service_l = {"ietf-l2vpn-svc:vpn-service": vpn_service_list} + response_service_creation = None + conn_info = [] + self.logger.info("Sending vpn-service :{}".format(vpn_service_l)) + + try: + endpoint_service_creation = ( + "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format( + self.wim["wim_url"] ) + ) + response_service_creation = requests.post( + endpoint_service_creation, + headers=self.headers, + json=vpn_service_l, + auth=self.auth, + ) + except requests.exceptions.ConnectionError: + raise SdnConnectorError( + "Request to create service Timeout", http_code=408 + ) + + if response_service_creation.status_code == 409: + raise SdnConnectorError( + "Service already exists", + http_code=response_service_creation.status_code, + ) + elif response_service_creation.status_code != requests.codes.created: + raise SdnConnectorError( + "Request to create service not accepted", + http_code=response_service_creation.status_code, + ) + + """Second step, create the connections and vpn attachments""" + for connection_point in connection_points: + connection_point_wan_info = self.search_mapp(connection_point) + site_network_access = {} + connection = {} + + if connection_point["service_endpoint_encapsulation_type"] != "none": + if ( + connection_point["service_endpoint_encapsulation_type"] + == "dot1q" + ): + """The connection is a VLAN""" + connection["encapsulation-type"] = "dot1q-vlan-tagged" + tagged = {} + tagged_interf = {} + service_endpoint_encapsulation_info = connection_point[ + "service_endpoint_encapsulation_info" + ] + + if service_endpoint_encapsulation_info["vlan"] is None: + raise SdnConnectorError("VLAN must be provided") - if len(connection_points) < 2: - raise SdnConnectorError("Connections must be of at least 2 endpoints") - - """First step, create the vpn service""" - uuid_l2vpn = str(uuid.uuid4()) - vpn_service = {} - vpn_service["vpn-id"] = uuid_l2vpn - vpn_service["vpn-svc-type"] = "vpws" # Rename "vpn-scv-type" -> "vpn-svc-type" - vpn_service["svc-topo"] = "any-to-any" - vpn_service["customer-name"] = "osm" - vpn_service_list = [] - vpn_service_list.append(vpn_service) - vpn_service_l = {"ietf-l2vpn-svc:vpn-service": vpn_service_list} - response_service_creation = None - conn_info = [] - self.logger.info("Sending vpn-service :{}".format(vpn_service_l)) + tagged_interf["cvlan-id"] = service_endpoint_encapsulation_info[ + "vlan" + ] + tagged["dot1q-vlan-tagged"] = tagged_interf + connection["tagged-interface"] = tagged + else: + raise NotImplementedError("Encapsulation type not implemented") + + site_network_access["connection"] = connection + self.logger.info("Sending connection:{}".format(connection)) + vpn_attach = {} + vpn_attach["vpn-id"] = uuid_l2vpn + vpn_attach["site-role"] = vpn_service["svc-topo"] + "-role" + site_network_access["vpn-attachment"] = vpn_attach + self.logger.info("Sending vpn-attachement :{}".format(vpn_attach)) + uuid_sna = str(uuid.uuid4()) + site_network_access["network-access-id"] = uuid_sna + site_network_access["bearer"] = connection_point_wan_info[ + "service_mapping_info" + ]["bearer"] + site_network_accesses = {} + site_network_access_list = [] + site_network_access_list.append(site_network_access) + site_network_accesses[ + "ietf-l2vpn-svc:site-network-access" + ] = site_network_access_list + conn_info_d = {} + conn_info_d["site"] = connection_point_wan_info["service_mapping_info"][ + "site-id" + ] + conn_info_d["site-network-access-id"] = site_network_access[ + "network-access-id" + ] + conn_info_d["mapping"] = None + conn_info.append(conn_info_d) try: - endpoint_service_creation = ( - "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/vpn-services".format( - self.wim["wim_url"] + endpoint_site_network_access_creation = ( + "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/" + "sites/site={}/site-network-accesses/".format( + self.wim["wim_url"], + connection_point_wan_info["service_mapping_info"][ + "site-id" + ], ) ) - response_service_creation = requests.post( - endpoint_service_creation, + response_endpoint_site_network_access_creation = requests.post( + endpoint_site_network_access_creation, headers=self.headers, - json=vpn_service_l, + json=site_network_accesses, auth=self.auth, ) - except requests.exceptions.ConnectionError: - raise SdnConnectorError( - "Request to create service Timeout", http_code=408 - ) - if response_service_creation.status_code == 409: - raise SdnConnectorError( - "Service already exists", - http_code=response_service_creation.status_code, - ) - elif response_service_creation.status_code != requests.codes.created: - raise SdnConnectorError( - "Request to create service not accepted", - http_code=response_service_creation.status_code, - ) + if ( + response_endpoint_site_network_access_creation.status_code + == 409 + ): + self.delete_connectivity_service(vpn_service["vpn-id"]) + + raise SdnConnectorError( + "Site_Network_Access with ID '{}' already exists".format( + site_network_access["network-access-id"] + ), + http_code=response_endpoint_site_network_access_creation.status_code, + ) + elif ( + response_endpoint_site_network_access_creation.status_code + == 400 + ): + self.delete_connectivity_service(vpn_service["vpn-id"]) - """Second step, create the connections and vpn attachments""" - for connection_point in connection_points: - connection_point_wan_info = self.search_mapp(connection_point) - site_network_access = {} - connection = {} - - if connection_point["service_endpoint_encapsulation_type"] != "none": - if ( - connection_point["service_endpoint_encapsulation_type"] - == "dot1q" - ): - """The connection is a VLAN""" - connection["encapsulation-type"] = "dot1q-vlan-tagged" - tagged = {} - tagged_interf = {} - service_endpoint_encapsulation_info = connection_point[ - "service_endpoint_encapsulation_info" - ] - - if service_endpoint_encapsulation_info["vlan"] is None: - raise SdnConnectorError("VLAN must be provided") - - tagged_interf["cvlan-id"] = service_endpoint_encapsulation_info[ - "vlan" - ] - tagged["dot1q-vlan-tagged"] = tagged_interf - connection["tagged-interface"] = tagged - else: - raise NotImplementedError("Encapsulation type not implemented") - - site_network_access["connection"] = connection - self.logger.info("Sending connection:{}".format(connection)) - vpn_attach = {} - vpn_attach["vpn-id"] = uuid_l2vpn - vpn_attach["site-role"] = vpn_service["svc-topo"] + "-role" - site_network_access["vpn-attachment"] = vpn_attach - self.logger.info("Sending vpn-attachement :{}".format(vpn_attach)) - uuid_sna = str(uuid.uuid4()) - site_network_access["network-access-id"] = uuid_sna - site_network_access["bearer"] = connection_point_wan_info[ - "service_mapping_info" - ]["bearer"] - site_network_accesses = {} - site_network_access_list = [] - site_network_access_list.append(site_network_access) - site_network_accesses[ - "ietf-l2vpn-svc:site-network-access" - ] = site_network_access_list - conn_info_d = {} - conn_info_d["site"] = connection_point_wan_info["service_mapping_info"][ - "site-id" - ] - conn_info_d["site-network-access-id"] = site_network_access[ - "network-access-id" - ] - conn_info_d["mapping"] = None - conn_info.append(conn_info_d) - - try: - endpoint_site_network_access_creation = ( - "{}/restconf/data/ietf-l2vpn-svc:l2vpn-svc/" - "sites/site={}/site-network-accesses/".format( - self.wim["wim_url"], + raise SdnConnectorError( + "Site {} does not exist".format( connection_point_wan_info["service_mapping_info"][ "site-id" - ], - ) - ) - response_endpoint_site_network_access_creation = requests.post( - endpoint_site_network_access_creation, - headers=self.headers, - json=site_network_accesses, - auth=self.auth, + ] + ), + http_code=response_endpoint_site_network_access_creation.status_code, ) - - if ( - response_endpoint_site_network_access_creation.status_code - == 409 - ): - self.delete_connectivity_service(vpn_service["vpn-id"]) - - raise SdnConnectorError( - "Site_Network_Access with ID '{}' already exists".format( - site_network_access["network-access-id"] - ), - http_code=response_endpoint_site_network_access_creation.status_code, - ) - elif ( - response_endpoint_site_network_access_creation.status_code - == 400 - ): - self.delete_connectivity_service(vpn_service["vpn-id"]) - - raise SdnConnectorError( - "Site {} does not exist".format( - connection_point_wan_info["service_mapping_info"][ - "site-id" - ] - ), - http_code=response_endpoint_site_network_access_creation.status_code, - ) - elif ( - response_endpoint_site_network_access_creation.status_code - != requests.codes.created - and response_endpoint_site_network_access_creation.status_code - != requests.codes.no_content - ): - self.delete_connectivity_service(vpn_service["vpn-id"]) - - raise SdnConnectorError( - "Request no accepted", - http_code=response_endpoint_site_network_access_creation.status_code, - ) - except requests.exceptions.ConnectionError: + elif ( + response_endpoint_site_network_access_creation.status_code + != requests.codes.created + and response_endpoint_site_network_access_creation.status_code + != requests.codes.no_content + ): self.delete_connectivity_service(vpn_service["vpn-id"]) - raise SdnConnectorError("Request Timeout", http_code=408) + raise SdnConnectorError( + "Request no accepted", + http_code=response_endpoint_site_network_access_creation.status_code, + ) + except requests.exceptions.ConnectionError: + self.delete_connectivity_service(vpn_service["vpn-id"]) + + raise SdnConnectorError("Request Timeout", http_code=408) - return uuid_l2vpn, conn_info - else: - raise NotImplementedError + return uuid_l2vpn, conn_info def delete_connectivity_service(self, service_uuid, conn_info=None): """Disconnect multi-site endpoints previously connected diff --git a/src/context/client/ContextClient.py b/src/context/client/ContextClient.py index bf58ea45d..3206e4a36 100644 --- a/src/context/client/ContextClient.py +++ b/src/context/client/ContextClient.py @@ -15,11 +15,12 @@ from typing import Iterator import grpc, logging from common.tools.client.RetryDecorator import retry, delay_exponential +from common.tools.grpc.Tools import grpc_message_to_json_string from context.proto.context_pb2 import ( Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, Context, ContextEvent, ContextId, ContextIdList, ContextList, Device, DeviceEvent, DeviceId, DeviceIdList, DeviceList, Empty, Link, LinkEvent, - LinkId, LinkIdList, LinkList, Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList, Topology, - TopologyEvent, TopologyId, TopologyIdList, TopologyList) + LinkId, LinkIdList, LinkList, Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList, Slice, SliceEvent, + SliceId, SliceIdList, SliceList, Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList) from context.proto.context_pb2_grpc import ContextServiceStub LOGGER = logging.getLogger(__name__) @@ -47,252 +48,294 @@ class ContextClient: @RETRY_DECORATOR def ListContextIds(self, request: Empty) -> ContextIdList: - LOGGER.debug('ListContextIds request: {:s}'.format(str(request))) + LOGGER.debug('ListContextIds request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.ListContextIds(request) - LOGGER.debug('ListContextIds result: {:s}'.format(str(response))) + LOGGER.debug('ListContextIds result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def ListContexts(self, request: Empty) -> ContextList: - LOGGER.debug('ListContexts request: {:s}'.format(str(request))) + LOGGER.debug('ListContexts request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.ListContexts(request) - LOGGER.debug('ListContexts result: {:s}'.format(str(response))) + LOGGER.debug('ListContexts result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetContext(self, request: ContextId) -> Context: - LOGGER.debug('GetContext request: {:s}'.format(str(request))) + LOGGER.debug('GetContext request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetContext(request) - LOGGER.debug('GetContext result: {:s}'.format(str(response))) + LOGGER.debug('GetContext result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def SetContext(self, request: Context) -> ContextId: - LOGGER.debug('SetContext request: {:s}'.format(str(request))) + LOGGER.debug('SetContext request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.SetContext(request) - LOGGER.debug('SetContext result: {:s}'.format(str(response))) + LOGGER.debug('SetContext result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def RemoveContext(self, request: ContextId) -> Empty: - LOGGER.debug('RemoveContext request: {:s}'.format(str(request))) + LOGGER.debug('RemoveContext request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.RemoveContext(request) - LOGGER.debug('RemoveContext result: {:s}'.format(str(response))) + LOGGER.debug('RemoveContext result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetContextEvents(self, request: Empty) -> Iterator[ContextEvent]: - LOGGER.debug('GetContextEvents request: {:s}'.format(str(request))) + LOGGER.debug('GetContextEvents request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetContextEvents(request) - LOGGER.debug('GetContextEvents result: {:s}'.format(str(response))) + LOGGER.debug('GetContextEvents result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def ListTopologyIds(self, request: ContextId) -> TopologyIdList: - LOGGER.debug('ListTopologyIds request: {:s}'.format(str(request))) + LOGGER.debug('ListTopologyIds request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.ListTopologyIds(request) - LOGGER.debug('ListTopologyIds result: {:s}'.format(str(response))) + LOGGER.debug('ListTopologyIds result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def ListTopologies(self, request: ContextId) -> TopologyList: - LOGGER.debug('ListTopologies request: {:s}'.format(str(request))) + LOGGER.debug('ListTopologies request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.ListTopologies(request) - LOGGER.debug('ListTopologies result: {:s}'.format(str(response))) + LOGGER.debug('ListTopologies result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetTopology(self, request: TopologyId) -> Topology: - LOGGER.debug('GetTopology request: {:s}'.format(str(request))) + LOGGER.debug('GetTopology request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetTopology(request) - LOGGER.debug('GetTopology result: {:s}'.format(str(response))) + LOGGER.debug('GetTopology result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def SetTopology(self, request: Topology) -> TopologyId: - LOGGER.debug('SetTopology request: {:s}'.format(str(request))) + LOGGER.debug('SetTopology request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.SetTopology(request) - LOGGER.debug('SetTopology result: {:s}'.format(str(response))) + LOGGER.debug('SetTopology result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def RemoveTopology(self, request: TopologyId) -> Empty: - LOGGER.debug('RemoveTopology request: {:s}'.format(str(request))) + LOGGER.debug('RemoveTopology request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.RemoveTopology(request) - LOGGER.debug('RemoveTopology result: {:s}'.format(str(response))) + LOGGER.debug('RemoveTopology result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetTopologyEvents(self, request: Empty) -> Iterator[TopologyEvent]: - LOGGER.debug('GetTopologyEvents request: {:s}'.format(str(request))) + LOGGER.debug('GetTopologyEvents request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetTopologyEvents(request) - LOGGER.debug('GetTopologyEvents result: {:s}'.format(str(response))) + LOGGER.debug('GetTopologyEvents result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def ListDeviceIds(self, request: Empty) -> DeviceIdList: - LOGGER.debug('ListDeviceIds request: {:s}'.format(str(request))) + LOGGER.debug('ListDeviceIds request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.ListDeviceIds(request) - LOGGER.debug('ListDeviceIds result: {:s}'.format(str(response))) + LOGGER.debug('ListDeviceIds result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def ListDevices(self, request: Empty) -> DeviceList: - LOGGER.debug('ListDevices request: {:s}'.format(str(request))) + LOGGER.debug('ListDevices request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.ListDevices(request) - LOGGER.debug('ListDevices result: {:s}'.format(str(response))) + LOGGER.debug('ListDevices result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetDevice(self, request: DeviceId) -> Device: - LOGGER.debug('GetDevice request: {:s}'.format(str(request))) + LOGGER.debug('GetDevice request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetDevice(request) - LOGGER.debug('GetDevice result: {:s}'.format(str(response))) + LOGGER.debug('GetDevice result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def SetDevice(self, request: Device) -> DeviceId: - LOGGER.debug('SetDevice request: {:s}'.format(str(request))) + LOGGER.debug('SetDevice request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.SetDevice(request) - LOGGER.debug('SetDevice result: {:s}'.format(str(response))) + LOGGER.debug('SetDevice result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def RemoveDevice(self, request: DeviceId) -> Empty: - LOGGER.debug('RemoveDevice request: {:s}'.format(str(request))) + LOGGER.debug('RemoveDevice request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.RemoveDevice(request) - LOGGER.debug('RemoveDevice result: {:s}'.format(str(response))) + LOGGER.debug('RemoveDevice result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetDeviceEvents(self, request: Empty) -> Iterator[DeviceEvent]: - LOGGER.debug('GetDeviceEvents request: {:s}'.format(str(request))) + LOGGER.debug('GetDeviceEvents request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetDeviceEvents(request) - LOGGER.debug('GetDeviceEvents result: {:s}'.format(str(response))) + LOGGER.debug('GetDeviceEvents result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def ListLinkIds(self, request: Empty) -> LinkIdList: - LOGGER.debug('ListLinkIds request: {:s}'.format(str(request))) + LOGGER.debug('ListLinkIds request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.ListLinkIds(request) - LOGGER.debug('ListLinkIds result: {:s}'.format(str(response))) + LOGGER.debug('ListLinkIds result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def ListLinks(self, request: Empty) -> LinkList: - LOGGER.debug('ListLinks request: {:s}'.format(str(request))) + LOGGER.debug('ListLinks request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.ListLinks(request) - LOGGER.debug('ListLinks result: {:s}'.format(str(response))) + LOGGER.debug('ListLinks result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetLink(self, request: LinkId) -> Link: - LOGGER.debug('GetLink request: {:s}'.format(str(request))) + LOGGER.debug('GetLink request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetLink(request) - LOGGER.debug('GetLink result: {:s}'.format(str(response))) + LOGGER.debug('GetLink result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def SetLink(self, request: Link) -> LinkId: - LOGGER.debug('SetLink request: {:s}'.format(str(request))) + LOGGER.debug('SetLink request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.SetLink(request) - LOGGER.debug('SetLink result: {:s}'.format(str(response))) + LOGGER.debug('SetLink result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def RemoveLink(self, request: LinkId) -> Empty: - LOGGER.debug('RemoveLink request: {:s}'.format(str(request))) + LOGGER.debug('RemoveLink request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.RemoveLink(request) - LOGGER.debug('RemoveLink result: {:s}'.format(str(response))) + LOGGER.debug('RemoveLink result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetLinkEvents(self, request: Empty) -> Iterator[LinkEvent]: - LOGGER.debug('GetLinkEvents request: {:s}'.format(str(request))) + LOGGER.debug('GetLinkEvents request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetLinkEvents(request) - LOGGER.debug('GetLinkEvents result: {:s}'.format(str(response))) + LOGGER.debug('GetLinkEvents result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def ListServiceIds(self, request: ContextId) -> ServiceIdList: - LOGGER.debug('ListServiceIds request: {:s}'.format(str(request))) + LOGGER.debug('ListServiceIds request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.ListServiceIds(request) - LOGGER.debug('ListServiceIds result: {:s}'.format(str(response))) + LOGGER.debug('ListServiceIds result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def ListServices(self, request: ContextId) -> ServiceList: - LOGGER.debug('ListServices request: {:s}'.format(str(request))) + LOGGER.debug('ListServices request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.ListServices(request) - LOGGER.debug('ListServices result: {:s}'.format(str(response))) + LOGGER.debug('ListServices result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetService(self, request: ServiceId) -> Service: - LOGGER.debug('GetService request: {:s}'.format(str(request))) + LOGGER.debug('GetService request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetService(request) - LOGGER.debug('GetService result: {:s}'.format(str(response))) + LOGGER.debug('GetService result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def SetService(self, request: Service) -> ServiceId: - LOGGER.debug('SetService request: {:s}'.format(str(request))) + LOGGER.debug('SetService request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.SetService(request) - LOGGER.debug('SetService result: {:s}'.format(str(response))) + LOGGER.debug('SetService result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def RemoveService(self, request: ServiceId) -> Empty: - LOGGER.debug('RemoveService request: {:s}'.format(str(request))) + LOGGER.debug('RemoveService request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.RemoveService(request) - LOGGER.debug('RemoveService result: {:s}'.format(str(response))) + LOGGER.debug('RemoveService result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetServiceEvents(self, request: Empty) -> Iterator[ServiceEvent]: - LOGGER.debug('GetServiceEvents request: {:s}'.format(str(request))) + LOGGER.debug('GetServiceEvents request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetServiceEvents(request) - LOGGER.debug('GetServiceEvents result: {:s}'.format(str(response))) + LOGGER.debug('GetServiceEvents result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def ListSliceIds(self, request: ContextId) -> SliceIdList: + LOGGER.debug('ListSliceIds request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.ListSliceIds(request) + LOGGER.debug('ListSliceIds result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def ListSlices(self, request: ContextId) -> SliceList: + LOGGER.debug('ListSlices request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.ListSlices(request) + LOGGER.debug('ListSlices result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def GetSlice(self, request: SliceId) -> Slice: + LOGGER.debug('GetSlice request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.GetSlice(request) + LOGGER.debug('GetSlice result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def SetSlice(self, request: Slice) -> SliceId: + LOGGER.debug('SetSlice request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.SetSlice(request) + LOGGER.debug('SetSlice result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def RemoveSlice(self, request: SliceId) -> Empty: + LOGGER.debug('RemoveSlice request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.RemoveSlice(request) + LOGGER.debug('RemoveSlice result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def GetSliceEvents(self, request: Empty) -> Iterator[SliceEvent]: + LOGGER.debug('GetSliceEvents request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.GetSliceEvents(request) + LOGGER.debug('GetSliceEvents result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def ListConnectionIds(self, request: ServiceId) -> ConnectionIdList: - LOGGER.debug('ListConnectionIds request: {:s}'.format(str(request))) + LOGGER.debug('ListConnectionIds request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.ListConnectionIds(request) - LOGGER.debug('ListConnectionIds result: {:s}'.format(str(response))) + LOGGER.debug('ListConnectionIds result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def ListConnections(self, request: ServiceId) -> ConnectionList: - LOGGER.debug('ListConnections request: {:s}'.format(str(request))) + LOGGER.debug('ListConnections request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.ListConnections(request) - LOGGER.debug('ListConnections result: {:s}'.format(str(response))) + LOGGER.debug('ListConnections result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetConnection(self, request: ConnectionId) -> Connection: - LOGGER.debug('GetConnection request: {:s}'.format(str(request))) + LOGGER.debug('GetConnection request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetConnection(request) - LOGGER.debug('GetConnection result: {:s}'.format(str(response))) + LOGGER.debug('GetConnection result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def SetConnection(self, request: Connection) -> ConnectionId: - LOGGER.debug('SetConnection request: {:s}'.format(str(request))) + LOGGER.debug('SetConnection request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.SetConnection(request) - LOGGER.debug('SetConnection result: {:s}'.format(str(response))) + LOGGER.debug('SetConnection result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def RemoveConnection(self, request: ConnectionId) -> Empty: - LOGGER.debug('RemoveConnection request: {:s}'.format(str(request))) + LOGGER.debug('RemoveConnection request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.RemoveConnection(request) - LOGGER.debug('RemoveConnection result: {:s}'.format(str(response))) + LOGGER.debug('RemoveConnection result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def GetConnectionEvents(self, request: Empty) -> Iterator[ConnectionEvent]: - LOGGER.debug('GetConnectionEvents request: {:s}'.format(str(request))) + LOGGER.debug('GetConnectionEvents request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.GetConnectionEvents(request) - LOGGER.debug('GetConnectionEvents result: {:s}'.format(str(response))) + LOGGER.debug('GetConnectionEvents result: {:s}'.format(grpc_message_to_json_string(response))) return response diff --git a/src/context/client/EventsCollector.py b/src/context/client/EventsCollector.py index 3022df0a6..f35b43eab 100644 --- a/src/context/client/EventsCollector.py +++ b/src/context/client/EventsCollector.py @@ -32,6 +32,7 @@ class EventsCollector: self._device_stream = context_client_grpc.GetDeviceEvents(Empty()) self._link_stream = context_client_grpc.GetLinkEvents(Empty()) self._service_stream = context_client_grpc.GetServiceEvents(Empty()) + self._slice_stream = context_client_grpc.GetSliceEvents(Empty()) self._connection_stream = context_client_grpc.GetConnectionEvents(Empty()) self._context_thread = threading.Thread(target=self._collect, args=(self._context_stream ,), daemon=False) @@ -39,6 +40,7 @@ class EventsCollector: self._device_thread = threading.Thread(target=self._collect, args=(self._device_stream ,), daemon=False) self._link_thread = threading.Thread(target=self._collect, args=(self._link_stream ,), daemon=False) self._service_thread = threading.Thread(target=self._collect, args=(self._service_stream ,), daemon=False) + self._slice_thread = threading.Thread(target=self._collect, args=(self._slice_stream ,), daemon=False) self._connection_thread = threading.Thread(target=self._collect, args=(self._connection_stream,), daemon=False) def _collect(self, events_stream) -> None: @@ -57,6 +59,7 @@ class EventsCollector: self._device_thread.start() self._link_thread.start() self._service_thread.start() + self._slice_thread.start() self._connection_thread.start() def get_event(self, block : bool = True, timeout : float = 0.1): @@ -85,6 +88,7 @@ class EventsCollector: self._device_stream.cancel() self._link_stream.cancel() self._service_stream.cancel() + self._slice_stream.cancel() self._connection_stream.cancel() self._context_thread.join() @@ -92,4 +96,5 @@ class EventsCollector: self._device_thread.join() self._link_thread.join() self._service_thread.join() + self._slice_thread.join() self._connection_thread.join() diff --git a/src/context/service/database/SliceModel.py b/src/context/service/database/SliceModel.py index d5c7486f1..5b560a948 100644 --- a/src/context/service/database/SliceModel.py +++ b/src/context/service/database/SliceModel.py @@ -81,5 +81,5 @@ class SliceModel(Model): if include_endpoint_ids: result['slice_endpoint_ids'] = self.dump_endpoint_ids() if include_constraints: result['slice_constraints'] = self.dump_constraints() if include_service_ids: result['slice_service_ids'] = self.dump_service_ids() - if include_subslice_ids: result['sub_subslice_ids'] = self.dump_subslice_ids() + if include_subslice_ids: result['slice_subslice_ids'] = self.dump_subslice_ids() return result diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py index 223c0466a..9218d550f 100644 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py @@ -609,7 +609,7 @@ class ContextServiceServicerImpl(ContextServiceServicer): @safe_and_metered_rpc_method(METRICS, LOGGER) def SetSlice(self, request: Slice, context : grpc.ServicerContext) -> SliceId: with self.lock: - context_uuid = request.service_id.context_id.context_uuid.uuid + context_uuid = request.slice_id.context_id.context_uuid.uuid db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) for i,endpoint_id in enumerate(request.slice_endpoint_ids): diff --git a/src/interdomain/Config.py b/src/interdomain/Config.py index 2e236fa9f..ee5cd0411 100644 --- a/src/interdomain/Config.py +++ b/src/interdomain/Config.py @@ -26,7 +26,8 @@ GRPC_GRACE_PERIOD = 60 METRICS_PORT = 9192 # Dependency micro-service connection settings +CONTEXT_SERVICE_HOST = '127.0.0.1' +CONTEXT_SERVICE_PORT = 1010 + SLICE_SERVICE_HOST = '127.0.0.1' SLICE_SERVICE_PORT = 4040 - - diff --git a/src/interdomain/client/InterdomainClient.py b/src/interdomain/client/InterdomainClient.py index fc61496cf..985af9c53 100644 --- a/src/interdomain/client/InterdomainClient.py +++ b/src/interdomain/client/InterdomainClient.py @@ -1,7 +1,7 @@ import grpc, logging from common.tools.client.RetryDecorator import retry, delay_exponential -from interdomain.proto.context_pb2 import TeraFlowController, AuthenticationResult -from interdomain.proto.slice_pb2 import TransportSlice, SliceId +from common.tools.grpc.Tools import grpc_message_to_json_string +from interdomain.proto.context_pb2 import AuthenticationResult, Slice, SliceId, SliceStatus, TeraFlowController from interdomain.proto.interdomain_pb2_grpc import InterdomainServiceStub LOGGER = logging.getLogger(__name__) @@ -27,30 +27,37 @@ class InterdomainClient: self.channel = None self.stub = None + @RETRY_DECORATOR + def RequestSlice(self, request : Slice) -> SliceId: + LOGGER.debug('RequestSlice request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.RequestSlice(request) + LOGGER.debug('RequestSlice result: {:s}'.format(grpc_message_to_json_string(response))) + return response + @RETRY_DECORATOR def Authenticate(self, request : TeraFlowController) -> AuthenticationResult: - LOGGER.debug('Authenticate request: {:s}'.format(str(request))) + LOGGER.debug('Authenticate request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.Authenticate(request) - LOGGER.debug('Authenticate result: {:s}'.format(str(response))) + LOGGER.debug('Authenticate result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR - def LookUpSlice(self, request : TransportSlice) -> SliceId: - LOGGER.debug('LookUpSlice request: {:s}'.format(str(request))) + def LookUpSlice(self, request : Slice) -> SliceId: + LOGGER.debug('LookUpSlice request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.LookUpSlice(request) - LOGGER.debug('LookUpSlice result: {:s}'.format(str(response))) + LOGGER.debug('LookUpSlice result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR - def OrderSliceFromCatalog(self, request : TransportSlice) -> SliceStatus: - LOGGER.debug('OrderSliceFromCatalog request: {:s}'.format(str(request))) + def OrderSliceFromCatalog(self, request : Slice) -> SliceStatus: + LOGGER.debug('OrderSliceFromCatalog request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.OrderSliceFromCatalog(request) - LOGGER.debug('OrderSliceFromCatalog result: {:s}'.format(str(response))) + LOGGER.debug('OrderSliceFromCatalog result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR - def CreateSliceAndAddToCatalog(self, request : TransportSlice) -> SliceStatus: - LOGGER.debug('CreateSliceAndAddToCatalog request: {:s}'.format(str(request))) + def CreateSliceAndAddToCatalog(self, request : Slice) -> SliceStatus: + LOGGER.debug('CreateSliceAndAddToCatalog request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.CreateSliceAndAddToCatalog(request) - LOGGER.debug('CreateSliceAndAddToCatalog result: {:s}'.format(str(response))) + LOGGER.debug('CreateSliceAndAddToCatalog result: {:s}'.format(grpc_message_to_json_string(response))) return response diff --git a/src/interdomain/requirements.in b/src/interdomain/requirements.in index 58c398e7d..162ecde82 100644 --- a/src/interdomain/requirements.in +++ b/src/interdomain/requirements.in @@ -1,5 +1,6 @@ grpcio==1.43.0 grpcio-health-checking==1.43.0 prometheus-client==0.13.0 +protobuf==3.19.3 pytest==6.2.5 pytest-benchmark==3.4.1 diff --git a/src/interdomain/service/InterdomainService.py b/src/interdomain/service/InterdomainService.py index b1eaa635f..debc943cf 100644 --- a/src/interdomain/service/InterdomainService.py +++ b/src/interdomain/service/InterdomainService.py @@ -17,19 +17,24 @@ 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 context.client.ContextClient import ContextClient from interdomain.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD from interdomain.proto.interdomain_pb2_grpc import add_InterdomainServiceServicer_to_server +from slice.client.SliceClient import SliceClient from .InterdomainServiceServicerImpl import InterdomainServiceServicerImpl +from .RemoteDomainClients import RemoteDomainClients BIND_ADDRESS = '0.0.0.0' LOGGER = logging.getLogger(__name__) class InterdomainService: def __init__( - self, slice_client, - address=BIND_ADDRESS, port=GRPC_SERVICE_PORT, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD): - + self, context_client : ContextClient, slice_client : SliceClient, remote_domain_clients : RemoteDomainClients, + address=BIND_ADDRESS, port=GRPC_SERVICE_PORT, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD + ): + self.context_client = context_client self.slice_client = slice_client + self.remote_domain_clients = remote_domain_clients self.address = address self.port = port self.endpoint = None @@ -48,7 +53,8 @@ class InterdomainService: self.pool = futures.ThreadPoolExecutor(max_workers=self.max_workers) self.server = grpc.server(self.pool) # , interceptors=(tracer_interceptor,)) - self.interdomain_servicer = InterdomainServiceServicerImpl() + self.interdomain_servicer = InterdomainServiceServicerImpl( + self.context_client, self.slice_client, self.remote_domain_clients) add_InterdomainServiceServicer_to_server(self.interdomain_servicer, self.server) self.health_servicer = HealthServicer( diff --git a/src/interdomain/service/InterdomainServiceServicerImpl.py b/src/interdomain/service/InterdomainServiceServicerImpl.py index 8570651d7..e76297625 100644 --- a/src/interdomain/service/InterdomainServiceServicerImpl.py +++ b/src/interdomain/service/InterdomainServiceServicerImpl.py @@ -14,36 +14,139 @@ import grpc, logging from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method -from interdomain.proto.context_pb2 import AuthenticationResult, TeraFlowController -from interdomain.proto.slice_pb2 import SliceId, SliceStatus, TransportSlice +from common.tools.grpc.Tools import grpc_message_to_json_string +from context.client.ContextClient import ContextClient +from context.proto.context_pb2 import SliceStatusEnum +from interdomain.proto.context_pb2 import AuthenticationResult, Slice, SliceId, SliceStatus, TeraFlowController from interdomain.proto.interdomain_pb2_grpc import InterdomainServiceServicer +from interdomain.service.RemoteDomainClients import RemoteDomainClients +from slice.client.SliceClient import SliceClient LOGGER = logging.getLogger(__name__) SERVICE_NAME = 'Interdomain' -METHOD_NAMES = ['Authenticate', 'LookUpSlice', 'OrderSliceFromCatalog', 'CreateSliceAndAddToCatalog'] +METHOD_NAMES = ['RequestSlice', 'Authenticate', 'LookUpSlice', 'OrderSliceFromCatalog', 'CreateSliceAndAddToCatalog'] METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) class InterdomainServiceServicerImpl(InterdomainServiceServicer): - def __init__(self): + def __init__( + self, context_client : ContextClient, slice_client : SliceClient, + remote_domain_clients : RemoteDomainClients + ): LOGGER.debug('Creating Servicer...') + self.context_client = context_client + self.slice_client = slice_client + self.remote_domain_clients = remote_domain_clients LOGGER.debug('Servicer Created') + @safe_and_metered_rpc_method(METRICS, LOGGER) + def RequestSlice(self, request : Slice, context : grpc.ServicerContext) -> SliceId: + domains_to_endpoints = {} + local_domain_uuid = None + for slice_endpoint_id in request.slice_endpoint_ids: + device_uuid = slice_endpoint_id.device_id.device_uuid.uuid + domain_uuid = device_uuid.split('@')[1] + endpoints = domains_to_endpoints.setdefault(domain_uuid, []) + endpoints.append(slice_endpoint_id) + if local_domain_uuid is None: local_domain_uuid = domain_uuid + + reply = Slice() + reply.CopyFrom(request) + + # decompose remote slices + for domain_uuid, slice_endpoint_ids in domains_to_endpoints.items(): + if domain_uuid == local_domain_uuid: continue + + remote_slice_request = Slice() + remote_slice_request.slice_id.context_id.context_uuid.uuid = request.slice_id.context_id.context_uuid.uuid + remote_slice_request.slice_id.slice_uuid.uuid = \ + request.slice_id.slice_uuid.uuid + ':subslice@' + local_domain_uuid + remote_slice_request.slice_status.slice_status = request.slice_status.slice_status + for endpoint_id in slice_endpoint_ids: + slice_endpoint_id = remote_slice_request.slice_endpoint_ids.add() + slice_endpoint_id.device_id.device_uuid.uuid = endpoint_id.device_id.device_uuid.uuid + slice_endpoint_id.endpoint_uuid.uuid = endpoint_id.endpoint_uuid.uuid + + # add endpoint connecting to remote domain + if domain_uuid == 'D1': + slice_endpoint_id = remote_slice_request.slice_endpoint_ids.add() + slice_endpoint_id.device_id.device_uuid.uuid = 'R4@D1' + slice_endpoint_id.endpoint_uuid.uuid = '2/1' + elif domain_uuid == 'D2': + slice_endpoint_id = remote_slice_request.slice_endpoint_ids.add() + slice_endpoint_id.device_id.device_uuid.uuid = 'R1@D2' + slice_endpoint_id.endpoint_uuid.uuid = '2/1' + + interdomain_client = self.remote_domain_clients.get_peer('remote-teraflow') + remote_slice_reply = interdomain_client.LookUpSlice(remote_slice_request) + if remote_slice_reply == remote_slice_request.slice_id: # pylint: disable=no-member + # successful case + remote_slice = interdomain_client.OrderSliceFromCatalog(remote_slice_request) + if remote_slice.slice_status.slice_status != SliceStatusEnum.SLICESTATUS_ACTIVE: + raise Exception('Remote Slice creation failed. Wrong Slice status returned') + else: + # not in catalog + remote_slice = interdomain_client.CreateSliceAndAddToCatalog(remote_slice_request) + if remote_slice.slice_status.slice_status != SliceStatusEnum.SLICESTATUS_ACTIVE: + raise Exception('Remote Slice creation failed. Wrong Slice status returned') + + #self.context_client.SetSlice(remote_slice) + #subslice_id = reply.slice_subslice_ids.add() + #subslice_id.CopyFrom(remote_slice.slice_id) + + local_slice_request = Slice() + local_slice_request.slice_id.context_id.context_uuid.uuid = request.slice_id.context_id.context_uuid.uuid + local_slice_request.slice_id.slice_uuid.uuid = request.slice_id.slice_uuid.uuid + ':subslice' + local_slice_request.slice_status.slice_status = request.slice_status.slice_status + for endpoint_id in domains_to_endpoints[local_domain_uuid]: + slice_endpoint_id = local_slice_request.slice_endpoint_ids.add() + slice_endpoint_id.CopyFrom(endpoint_id) + + # add endpoint connecting to remote domain + if local_domain_uuid == 'D1': + slice_endpoint_id = local_slice_request.slice_endpoint_ids.add() + slice_endpoint_id.device_id.device_uuid.uuid = 'R4@D1' + slice_endpoint_id.endpoint_uuid.uuid = '2/1' + elif local_domain_uuid == 'D2': + slice_endpoint_id = local_slice_request.slice_endpoint_ids.add() + slice_endpoint_id.device_id.device_uuid.uuid = 'R1@D2' + slice_endpoint_id.endpoint_uuid.uuid = '2/1' + + local_slice_reply = self.slice_client.CreateSlice(local_slice_request) + if local_slice_reply != local_slice_request.slice_id: # pylint: disable=no-member + raise Exception('Local Slice creation failed. Wrong Slice Id was returned') + + subslice_id = reply.slice_subslice_ids.add() + subslice_id.context_id.context_uuid.uuid = local_slice_request.slice_id.context_id.context_uuid.uuid + subslice_id.slice_uuid.uuid = local_slice_request.slice_id.slice_uuid.uuid + + self.context_client.SetSlice(reply) + return reply.slice_id + @safe_and_metered_rpc_method(METRICS, LOGGER) def Authenticate(self, request : TeraFlowController, context : grpc.ServicerContext) -> AuthenticationResult: auth_result = AuthenticationResult() - #auth_result.context_id = ... + auth_result.context_id.CopyFrom(request.context_id) # pylint: disable=no-member auth_result.authenticated = True return auth_result @safe_and_metered_rpc_method(METRICS, LOGGER) - def LookUpSlice(self, request : TransportSlice, context : grpc.ServicerContext) -> SliceId: - return SliceId() + def LookUpSlice(self, request : Slice, context : grpc.ServicerContext) -> SliceId: + try: + slice_ = self.context_client.GetSlice(request.slice_id) + return slice_.slice_id + except grpc.RpcError: + #LOGGER.exception('Unable to get slice({:s})'.format(grpc_message_to_json_string(request.slice_id))) + return SliceId() @safe_and_metered_rpc_method(METRICS, LOGGER) - def OrderSliceFromCatalog(self, request : TransportSlice, context : grpc.ServicerContext) -> SliceStatus: - return SliceStatus() + def OrderSliceFromCatalog(self, request : Slice, context : grpc.ServicerContext) -> Slice: + raise NotImplementedError('OrderSliceFromCatalog') + #return Slice() @safe_and_metered_rpc_method(METRICS, LOGGER) - def CreateSliceAndAddToCatalog(self, request : TransportSlice, context : grpc.ServicerContext) -> SliceStatus: - return SliceStatus() + def CreateSliceAndAddToCatalog(self, request : Slice, context : grpc.ServicerContext) -> Slice: + reply = self.slice_client.CreateSlice(request) + if reply != request.slice_id: # pylint: disable=no-member + raise Exception('Slice creation failed. Wrong Slice Id was returned') + return self.context_client.GetSlice(request.slice_id) diff --git a/src/interdomain/service/RemoteDomainClients.py b/src/interdomain/service/RemoteDomainClients.py new file mode 100644 index 000000000..98c6a37d0 --- /dev/null +++ b/src/interdomain/service/RemoteDomainClients.py @@ -0,0 +1,42 @@ +import logging, socket +from common.Constants import DEFAULT_CONTEXT_UUID +from common.Settings import get_setting +from interdomain.Config import GRPC_SERVICE_PORT +from interdomain.client.InterdomainClient import InterdomainClient +from interdomain.proto.context_pb2 import TeraFlowController + +LOGGER = logging.getLogger(__name__) + +class RemoteDomainClients: + def __init__(self) -> None: + self.peer_domain = {} + + def add_peer( + self, domain_name : str, address : str, port : int, context_uuid : str = DEFAULT_CONTEXT_UUID + ) -> None: + while True: + try: + remote_teraflow_ip = socket.gethostbyname(address) + if len(remote_teraflow_ip) > 0: break + except socket.gaierror as e: + if str(e) == '[Errno -2] Name or service not known': continue + + interdomain_client = InterdomainClient(address, port) + request = TeraFlowController() + request.context_id.context_uuid.uuid = DEFAULT_CONTEXT_UUID # pylint: disable=no-member + request.ip_address = get_setting('INTERDOMAINSERVICE_SERVICE_HOST', default='0.0.0.0') + request.port = int(get_setting('INTERDOMAINSERVICE_SERVICE_PORT_GRPC', default=GRPC_SERVICE_PORT)) + + reply = interdomain_client.Authenticate(request) + if not reply.authenticated: + msg = 'Authentication against {:s}:{:d} rejected' + raise Exception(msg.format(str(remote_teraflow_ip), GRPC_SERVICE_PORT)) + + self.peer_domain[domain_name] = interdomain_client + + def get_peer(self, domain_name : str) -> InterdomainClient: + LOGGER.warning('peers: {:s}'.format(str(self.peer_domain))) + return self.peer_domain.get(domain_name) + + def remove_peer(self, domain_name : str) -> None: + return self.peer_domain.pop(domain_name, None) diff --git a/src/interdomain/service/__main__.py b/src/interdomain/service/__main__.py index 2b919870a..ff19271ee 100644 --- a/src/interdomain/service/__main__.py +++ b/src/interdomain/service/__main__.py @@ -15,10 +15,12 @@ import logging, signal, sys, threading from prometheus_client import start_http_server from common.Settings import get_setting, wait_for_environment_variables -from slice.client.SliceClient import SliceClient +from context.client.ContextClient import ContextClient +from interdomain.service.RemoteDomainClients import RemoteDomainClients from interdomain.Config import ( - SLICE_SERVICE_HOST, SLICE_SERVICE_PORT, GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, - METRICS_PORT) + CONTEXT_SERVICE_HOST, CONTEXT_SERVICE_PORT, SLICE_SERVICE_HOST, SLICE_SERVICE_PORT, GRPC_SERVICE_PORT, + GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, METRICS_PORT) +from slice.client.SliceClient import SliceClient from .InterdomainService import InterdomainService terminate = threading.Event() @@ -40,11 +42,13 @@ def main(): logging.basicConfig(level=log_level) LOGGER = logging.getLogger(__name__) - #wait_for_environment_variables([ - # 'CONTEXTSERVICE_SERVICE_HOST', 'CONTEXTSERVICE_SERVICE_PORT_GRPC', - # 'MONITORINGSERVICE_SERVICE_HOST', 'MONITORINGSERVICE_SERVICE_PORT_GRPC' - #]) + wait_for_environment_variables([ + 'CONTEXTSERVICE_SERVICE_HOST', 'CONTEXTSERVICE_SERVICE_PORT_GRPC', + 'SLICESERVICE_SERVICE_HOST', 'SLICESERVICE_SERVICE_PORT_GRPC', + ]) + context_service_host = get_setting('CONTEXTSERVICE_SERVICE_HOST', default=CONTEXT_SERVICE_HOST ) + context_service_port = get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC', default=CONTEXT_SERVICE_PORT ) slice_service_host = get_setting('SLICESERVICE_SERVICE_HOST', default=SLICE_SERVICE_HOST ) slice_service_port = get_setting('SLICESERVICE_SERVICE_PORT_GRPC', default=SLICE_SERVICE_PORT ) @@ -56,18 +60,29 @@ def main(): # Start metrics server start_http_server(metrics_port) - ## Initialize Slice Client - #if slice_service_host is None or slice_service_port is None: - # raise Exception('Wrong address({:s}):port({:s}) of Slice component'.format( - # str(slice_service_host), str(slice_service_port))) - #slice_client = SliceClient(slice_service_host, slice_service_port) + # Initialize Context Client + if context_service_host is None or context_service_port is None: + raise Exception('Wrong address({:s}):port({:s}) of Context component'.format( + str(context_service_host), str(context_service_port))) + context_client = ContextClient(context_service_host, context_service_port) + + # Initialize Slice Client + if slice_service_host is None or slice_service_port is None: + raise Exception('Wrong address({:s}):port({:s}) of Slice component'.format( + str(slice_service_host), str(slice_service_port))) + slice_client = SliceClient(slice_service_host, slice_service_port) + + # Define remote domain clients + remote_domain_clients = RemoteDomainClients() # Starting Interdomain service grpc_service = InterdomainService( - slice_client, port=grpc_service_port, max_workers=max_workers, + context_client, slice_client, remote_domain_clients, port=grpc_service_port, max_workers=max_workers, grace_period=grace_period) grpc_service.start() + remote_domain_clients.add_peer('remote-teraflow', 'remote-teraflow', GRPC_SERVICE_PORT) + # Wait for Ctrl+C or termination signal while not terminate.wait(timeout=0.1): pass diff --git a/src/monitoring/requirements.in b/src/monitoring/requirements.in index 77b66b794..df2f1cf89 100644 --- a/src/monitoring/requirements.in +++ b/src/monitoring/requirements.in @@ -1,16 +1,17 @@ -google-api-core -grpcio-health-checking -grpcio -opencensus[stackdriver] +#google-api-core +grpcio==1.43.0 +grpcio-health-checking==1.43.0 +#opencensus[stackdriver] python-json-logger -google-cloud-profiler -numpy -prometheus-client -pytest -pytest-benchmark +#google-cloud-profiler +#numpy +prometheus-client==0.13.0 +protobuf==3.19.3 +pytest==6.2.5 +pytest-benchmark==3.4.1 influxdb -redis -anytree -apscheduler -xmltodict -coverage +#redis==4.1.2 +#anytree==2.8.0 +#APScheduler==3.8.1 +#xmltodict==0.12.0 +coverage==6.3 diff --git a/src/service/client/ServiceClient.py b/src/service/client/ServiceClient.py index af489c0c6..a44842768 100644 --- a/src/service/client/ServiceClient.py +++ b/src/service/client/ServiceClient.py @@ -14,6 +14,7 @@ import grpc, logging from common.tools.client.RetryDecorator import retry, delay_exponential +from common.tools.grpc.Tools import grpc_message_to_json_string from service.proto.context_pb2 import Empty, Service, ServiceId from service.proto.service_pb2_grpc import ServiceServiceStub @@ -42,21 +43,21 @@ class ServiceClient: @RETRY_DECORATOR def CreateService(self, request : Service) -> ServiceId: - LOGGER.debug('CreateService request: {:s}'.format(str(request))) + LOGGER.debug('CreateService request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.CreateService(request) - LOGGER.debug('CreateService result: {:s}'.format(str(response))) + LOGGER.debug('CreateService result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def UpdateService(self, request : Service) -> ServiceId: - LOGGER.debug('UpdateService request: {:s}'.format(str(request))) + LOGGER.debug('UpdateService request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.UpdateService(request) - LOGGER.debug('UpdateService result: {:s}'.format(str(response))) + LOGGER.debug('UpdateService result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR def DeleteService(self, request : ServiceId) -> Empty: - LOGGER.debug('DeleteService request: {:s}'.format(str(request))) + LOGGER.debug('DeleteService request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.DeleteService(request) - LOGGER.debug('DeleteService result: {:s}'.format(str(response))) + LOGGER.debug('DeleteService result: {:s}'.format(grpc_message_to_json_string(response))) return response diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py index 2506a4206..772069932 100644 --- a/src/service/service/ServiceServiceServicerImpl.py +++ b/src/service/service/ServiceServiceServicerImpl.py @@ -52,6 +52,8 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): @safe_and_metered_rpc_method(METRICS, LOGGER) def CreateService(self, request : Service, context : grpc.ServicerContext) -> ServiceId: + LOGGER.info('[CreateService] begin ; request = {:s}'.format(grpc_message_to_json_string(request))) + service_id = request.service_id service_uuid = service_id.service_uuid.uuid service_context_uuid = service_id.context_id.context_uuid.uuid diff --git a/src/slice/Config.py b/src/slice/Config.py index 6787413a8..e6d770d00 100644 --- a/src/slice/Config.py +++ b/src/slice/Config.py @@ -4,9 +4,19 @@ import logging LOG_LEVEL = logging.WARNING # gRPC settings -GRPC_SERVICE_PORT = 2020 +GRPC_SERVICE_PORT = 4040 GRPC_MAX_WORKERS = 10 GRPC_GRACE_PERIOD = 60 # Prometheus settings METRICS_PORT = 9192 + +# Dependency micro-service connection settings +CONTEXT_SERVICE_HOST = '127.0.0.1' +CONTEXT_SERVICE_PORT = 1010 + +SERVICE_SERVICE_HOST = '127.0.0.1' +SERVICE_SERVICE_PORT = 3030 + +INTERDOMAIN_SERVICE_HOST = '127.0.0.1' +INTERDOMAIN_SERVICE_PORT = 10010 diff --git a/src/slice/Dockerfile b/src/slice/Dockerfile index 081d60ecd..d653bb217 100644 --- a/src/slice/Dockerfile +++ b/src/slice/Dockerfile @@ -29,6 +29,9 @@ RUN python3 -m pip install -r slice/requirements.in # Add files into working directory COPY common/. common +COPY context/. context +COPY interdomain/. interdomain +COPY service/. service COPY slice/. slice # Start slice service diff --git a/src/slice/_docs/old_conflicting_code.txt b/src/slice/_docs/old_conflicting_code.txt new file mode 100644 index 000000000..12bd9bbbc --- /dev/null +++ b/src/slice/_docs/old_conflicting_code.txt @@ -0,0 +1,176 @@ +src/common/tools/service/DeviceCheckers.py + import grpc + from common.database.api.Database import Database + from common.database.api.context.topology.device.Device import Device + from common.database.api.context.topology.device.Endpoint import Endpoint + from common.exceptions.ServiceException import ServiceException + + def check_device_exists(database : Database, context_id : str, topology_id : str, device_id : str) -> Device: + db_context = database.context(context_id).create() + db_topology = db_context.topology(topology_id).create() + if db_topology.devices.contains(device_id): return db_topology.device(device_id) + msg = 'Context({})/Topology({})/Device({}) does not exist in the database.' + msg = msg.format(context_id, topology_id, device_id) + raise ServiceException(grpc.StatusCode.NOT_FOUND, msg) + + +src/common/tools/service/LinkCheckers.py + import grpc + from common.database.api.Database import Database + from common.database.api.context.topology.link.Link import Link + from common.exceptions.ServiceException import ServiceException + + def check_link_exists(database : Database, context_id : str, topology_id : str, link_id : str) -> Link: + db_context = database.context(context_id).create() + db_topology = db_context.topology(topology_id).create() + if db_topology.links.contains(link_id): return db_topology.link(link_id) + msg = 'Context({})/Topology({})/Link({}) does not exist in the database.' + msg = msg.format(context_id, topology_id, link_id) + raise ServiceException(grpc.StatusCode.NOT_FOUND, msg) + + +src/common/tools/service/ServiceCheckers.py + import grpc + from common.database.api.Database import Database + from common.exceptions.ServiceException import ServiceException + + def check_service_exists(database : Database, context_id : str, service_id : str): + if not database.contexts.contains(context_id): + msg = 'Context({}) does not exist in the database.' + msg = msg.format(context_id) + raise ServiceException(grpc.StatusCode.NOT_FOUND, msg) + + db_context = database.context(context_id) + if db_context.services.contains(service_id): + return db_context.service(service_id) + + msg = 'Context({})/Service({}) does not exist in the database.' + msg = msg.format(context_id, service_id) + raise ServiceException(grpc.StatusCode.NOT_FOUND, msg) + + +src/device/service/Tools.py + 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.topology.device.Endpoint import Endpoint + from common.database.api.context.topology.device.OperationalStatus import OperationalStatus, \ + operationalstatus_enum_values, to_operationalstatus_enum + from common.exceptions.ServiceException import ServiceException + from common.tools.service.DeviceCheckers import check_device_endpoint_exists + from common.tools.service.EndpointIdCheckers import check_endpoint_id + from common.tools.service.EnumCheckers import check_enum + from common.tools.service.DeviceCheckers import check_device_exists, check_device_not_exists + from device.proto.context_pb2 import Device, DeviceId + + # For each method name, define acceptable device operational statuses. Empty set means accept all. + ACCEPTED_DEVICE_OPERATIONAL_STATUSES : Dict[str, Set[OperationalStatus]] = { + 'AddDevice': set([OperationalStatus.ENABLED, OperationalStatus.DISABLED]), + 'UpdateDevice': set([OperationalStatus.KEEP_STATE, OperationalStatus.ENABLED, OperationalStatus.DISABLED]), + } + + def _check_device_exists(method_name : str, database : Database, device_id : str): + if method_name in ['AddDevice']: + check_device_not_exists(database, DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID, device_id) + elif method_name in ['UpdateDevice', 'DeleteDevice']: + check_device_exists(database, DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID, device_id) + else: # pragma: no cover (test requires malforming the code) + msg = 'Unexpected condition: _check_device_exists(method_name={}, device_id={})' + msg = msg.format(str(method_name), str(device_id)) + raise ServiceException(grpc.StatusCode.UNIMPLEMENTED, msg) + + def _check_device_endpoint_exists_or_get_pointer( + method_name : str, database : Database, parent_name : str, device_id : str, endpoint_id : str) -> Endpoint: + + if method_name in ['AddDevice']: + db_context = database.context(DEFAULT_CONTEXT_ID) + db_topology = db_context.topology(DEFAULT_TOPOLOGY_ID) + db_device = db_topology.device(device_id) + return db_device.endpoint(endpoint_id) + elif method_name in ['UpdateDevice', 'DeleteDevice']: + return check_device_endpoint_exists( + database, parent_name, DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID, device_id, endpoint_id) + else: # pragma: no cover (test requires malforming the code) + msg = 'Unexpected condition: _check_device_endpoint_exists_or_get_pointer(method_name={}, ' \ + 'parent_name={}, device_id={}, endpoint_id={})' + msg = msg.format(str(method_name), str(parent_name), str(device_id), str(endpoint_id)) + raise ServiceException(grpc.StatusCode.UNIMPLEMENTED, msg) + + def check_device_operational_status(method_name : str, value : str) -> OperationalStatus: + return check_enum( + 'OperationalStatus', method_name, value, to_operationalstatus_enum, ACCEPTED_DEVICE_OPERATIONAL_STATUSES) + + def check_device_request( + method_name : str, request : Device, database : Database, logger : logging.Logger + ) -> Tuple[str, str, str, OperationalStatus, List[Tuple[Endpoint, str]]]: + + # ----- Parse attributes ------------------------------------------------------------------------------------------- + try: + device_id = chk_string ('device.device_id.device_id.uuid', + request.device_id.device_id.uuid, + allow_empty=False) + device_type = chk_string ('device.device_type', + request.device_type, + allow_empty=False) + device_config = chk_string ('device.device_config.device_config', + request.device_config.device_config, + allow_empty=True) + device_opstat = chk_options('device.devOperationalStatus', + request.devOperationalStatus, + operationalstatus_enum_values()) + except Exception as e: + logger.exception('Invalid arguments:') + raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, str(e)) + + device_opstat = check_device_operational_status(method_name, device_opstat) + + # ----- Check if device exists in database ------------------------------------------------------------------------- + _check_device_exists(method_name, database, device_id) + + # ----- Parse endpoints and check if they exist in the database as device endpoints -------------------------------- + add_topology_devices_endpoints : Dict[str, Dict[str, Set[str]]] = {} + db_endpoints__port_types : List[Tuple[Endpoint, str]] = [] + for endpoint_number,endpoint in enumerate(request.endpointList): + parent_name = 'Endpoint(#{}) of Context({})/Topology({})/Device({})' + parent_name = parent_name.format(endpoint_number, DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID, device_id) + + _, ep_device_id, ep_port_id = check_endpoint_id( + logger, endpoint_number, parent_name, endpoint.port_id, add_topology_devices_endpoints, + predefined_device_id=device_id, acceptable_device_ids=set([device_id]), + prevent_same_device_multiple_times=False) + + try: + ep_port_type = chk_string('endpoint[#{}].port_type'.format(endpoint_number), + endpoint.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_or_get_pointer( + method_name, database, parent_name, ep_device_id, ep_port_id) + db_endpoints__port_types.append((db_endpoint, ep_port_type)) + + return device_id, device_type, device_config, device_opstat, db_endpoints__port_types + + def check_device_id_request( + method_name : str, request : DeviceId, database : Database, logger : logging.Logger) -> str: + + # ----- Parse attributes ------------------------------------------------------------------------------------------- + try: + device_id = chk_string('device_id.device_id.uuid', + request.device_id.uuid, + allow_empty=False) + except Exception as e: + logger.exception('Invalid arguments:') + raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, str(e)) + + # ----- Check if device exists in database --------------------------------------------------------------------------- + _check_device_exists(method_name, database, device_id) + + return device_id + + +src/service/service/Tools.py diff --git a/src/slice/client/SliceClient.py b/src/slice/client/SliceClient.py index a056a7101..c30c28002 100644 --- a/src/slice/client/SliceClient.py +++ b/src/slice/client/SliceClient.py @@ -1,7 +1,7 @@ import grpc, logging from common.tools.client.RetryDecorator import retry, delay_exponential -from slice.proto.context_pb2 import Empty -from slice.proto.slice_pb2 import TransportSlice, SliceStatus +from common.tools.grpc.Tools import grpc_message_to_json_string +from slice.proto.context_pb2 import Empty, Slice, SliceId from slice.proto.slice_pb2_grpc import SliceServiceStub LOGGER = logging.getLogger(__name__) @@ -23,20 +23,27 @@ class SliceClient: self.stub = SliceServiceStub(self.channel) def close(self): - if(self.channel is not None): self.channel.close() + if self.channel is not None: self.channel.close() self.channel = None self.stub = None @RETRY_DECORATOR - def CreateUpdateSlice(self, request : TransportSlice) -> SliceStatus: - LOGGER.debug('CreateUpdateSlice request: {:s}'.format(str(request))) - response = self.stub.CreateUpdateSlice(request) - LOGGER.debug('CreateUpdateSlice result: {:s}'.format(str(response))) + def CreateSlice(self, request : Slice) -> SliceId: + LOGGER.debug('CreateSlice request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.CreateSlice(request) + LOGGER.debug('CreateSlice result: {:s}'.format(grpc_message_to_json_string(response))) return response @RETRY_DECORATOR - def DeleteSlice(self, request : TransportSlice) -> Empty: - LOGGER.debug('DeleteSlice request: {:s}'.format(str(request))) + def UpdateSlice(self, request : Slice) -> SliceId: + LOGGER.debug('UpdateSlice request: {:s}'.format(grpc_message_to_json_string(request))) + response = self.stub.UpdateSlice(request) + LOGGER.debug('UpdateSlice result: {:s}'.format(grpc_message_to_json_string(response))) + return response + + @RETRY_DECORATOR + def DeleteSlice(self, request : SliceId) -> Empty: + LOGGER.debug('DeleteSlice request: {:s}'.format(grpc_message_to_json_string(request))) response = self.stub.DeleteSlice(request) - LOGGER.debug('DeleteSlice result: {:s}'.format(str(response))) + LOGGER.debug('DeleteSlice result: {:s}'.format(grpc_message_to_json_string(response))) return response diff --git a/src/slice/requirements.in b/src/slice/requirements.in index 58c398e7d..162ecde82 100644 --- a/src/slice/requirements.in +++ b/src/slice/requirements.in @@ -1,5 +1,6 @@ grpcio==1.43.0 grpcio-health-checking==1.43.0 prometheus-client==0.13.0 +protobuf==3.19.3 pytest==6.2.5 pytest-benchmark==3.4.1 diff --git a/src/slice/service/SliceService.py b/src/slice/service/SliceService.py index cddc4efe9..a7ad26946 100644 --- a/src/slice/service/SliceService.py +++ b/src/slice/service/SliceService.py @@ -17,6 +17,9 @@ 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 context.client.ContextClient import ContextClient +from interdomain.client.InterdomainClient import InterdomainClient +from service.client.ServiceClient import ServiceClient 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 @@ -26,9 +29,12 @@ LOGGER = logging.getLogger(__name__) class SliceService: def __init__( - self, # database, + self, context_client : ContextClient, interdomain_client : InterdomainClient, service_client : ServiceClient, address=BIND_ADDRESS, port=GRPC_SERVICE_PORT, max_workers=GRPC_MAX_WORKERS, grace_period=GRPC_GRACE_PERIOD): - #self.database = database + + self.context_client = context_client + self.interdomain_client = interdomain_client + self.service_client = service_client self.address = address self.port = port self.endpoint = None @@ -48,8 +54,7 @@ class SliceService: self.server = grpc.server(self.pool) # , interceptors=(tracer_interceptor,)) self.slice_servicer = SliceServiceServicerImpl( - #self.database - ) + self.context_client, self.interdomain_client, self.service_client) add_SliceServiceServicer_to_server(self.slice_servicer, self.server) self.health_servicer = HealthServicer( diff --git a/src/slice/service/SliceServiceServicerImpl.py b/src/slice/service/SliceServiceServicerImpl.py index 702e0ce38..dcc93622a 100644 --- a/src/slice/service/SliceServiceServicerImpl.py +++ b/src/slice/service/SliceServiceServicerImpl.py @@ -12,27 +12,124 @@ # See the License for the specific language governing permissions and # limitations under the License. -import grpc, logging +import grpc, json, logging +from typing import Dict from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method -from slice.proto.context_pb2 import Empty -from slice.proto.slice_pb2 import SliceStatus, TransportSlice +from context.client.ContextClient import ContextClient +from context.proto.context_pb2 import ( + ConfigActionEnum, Empty, Service, ServiceStatusEnum, ServiceTypeEnum, Slice, SliceId, SliceStatusEnum) +from interdomain.client.InterdomainClient import InterdomainClient +from service.client.ServiceClient import ServiceClient from slice.proto.slice_pb2_grpc import SliceServiceServicer LOGGER = logging.getLogger(__name__) SERVICE_NAME = 'Slice' -METHOD_NAMES = ['CreateUpdateSlice', 'DeleteSlice'] +METHOD_NAMES = ['CreateSlice', 'UpdateSlice', 'DeleteSlice'] METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) class SliceServiceServicerImpl(SliceServiceServicer): - def __init__(self): + def __init__( + self, context_client : ContextClient, interdomain_client : InterdomainClient, service_client : ServiceClient + ): LOGGER.debug('Creating Servicer...') + self.context_client = context_client + self.interdomain_client = interdomain_client + self.service_client = service_client LOGGER.debug('Servicer Created') + def create_update(self, request : Slice) -> SliceId: + slice_id = self.context_client.SetSlice(request) + if len(request.slice_endpoint_ids) != 2: return slice_id + + domains = set() + for slice_endpoint_id in request.slice_endpoint_ids: + device_uuid = slice_endpoint_id.device_id.device_uuid.uuid + domains.add(device_uuid.split('@')[1]) + + is_multi_domain = len(domains) == 2 + if is_multi_domain: + slice_id = self.interdomain_client.RequestSlice(request) + else: + # pylint: disable=no-member + service_request = Service() + service_request.service_id.context_id.context_uuid.uuid = request.slice_id.context_id.context_uuid.uuid + service_request.service_id.service_uuid.uuid = request.slice_id.slice_uuid.uuid + service_request.service_type = ServiceTypeEnum.SERVICETYPE_L3NM + service_request.service_status.service_status = ServiceStatusEnum.SERVICESTATUS_PLANNED + + service_reply = self.service_client.CreateService(service_request) + if service_reply != service_request.service_id: # pylint: disable=no-member + raise Exception('Service creation failed. Wrong Service Id was returned') + + config_rule = service_request.service_config.config_rules.add() + config_rule.action = ConfigActionEnum.CONFIGACTION_SET + config_rule.resource_key = '/settings' + config_rule.resource_value = json.dumps( + {'mtu': 1512, 'address_families': ['IPV4'], 'bgp_as': 65000, 'bgp_route_target': '65000:333'}, + sort_keys=True) + + for slice_endpoint_id in request.slice_endpoint_ids: + device_uuid = slice_endpoint_id.device_id.device_uuid.uuid + endpoint_uuid = slice_endpoint_id.endpoint_uuid.uuid + + endpoint_id = service_request.service_endpoint_ids.add() + endpoint_id.device_id.device_uuid.uuid = device_uuid + endpoint_id.endpoint_uuid.uuid = endpoint_uuid + + config_rule = service_request.service_config.config_rules.add() + config_rule.action = ConfigActionEnum.CONFIGACTION_SET + config_rule.resource_key = '/device[{:s}]/endpoint[{:s}]/settings'.format(device_uuid, endpoint_uuid) + config_rule.resource_value = json.dumps( + {'router_id': '0.0.0.0', 'route_distinguisher': '0:0', 'sub_interface_index': 0, 'vlan_id': 0, + 'address_ip': '0.0.0.0', 'address_prefix': 0}, + sort_keys=True) + + service_reply = self.service_client.UpdateService(service_request) + if service_reply != service_request.service_id: # pylint: disable=no-member + raise Exception('Service update failed. Wrong Service Id was returned') + + reply = Slice() + reply.CopyFrom(request) + slice_service_id = reply.slice_service_ids.add() + slice_service_id.CopyFrom(service_reply) + self.context_client.SetSlice(reply) + slice_id = reply.slice_id + + slice_ = self.context_client.GetSlice(slice_id) + slice_active = Slice() + slice_active.CopyFrom(slice_) + slice_active.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_ACTIVE + self.context_client.SetSlice(slice_active) + return slice_id + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def CreateSlice(self, request : Slice, context : grpc.ServicerContext) -> SliceId: + #try: + # slice_ = self.context_client.GetSlice(request.slice_id) + # slice_id = slice_.slice_id + #except grpc.RpcError: + # slice_id = self.context_client.SetSlice(request) + #return slice_id + return self.create_update(request) + @safe_and_metered_rpc_method(METRICS, LOGGER) - def CreateUpdateSlice(self, request : TransportSlice, context : grpc.ServicerContext) -> SliceStatus: - return SliceStatus() + def UpdateSlice(self, request : Slice, context : grpc.ServicerContext) -> SliceId: + #slice_id = self.context_client.SetSlice(request) + #if len(request.slice_endpoint_ids) != 2: return slice_id + # + #domains = set() + #for slice_endpoint_id in request.slice_endpoint_ids: + # device_uuid = slice_endpoint_id.device_id.device_uuid.uuid + # domains.add(device_uuid.split('@')[0]) + # + #is_multi_domain = len(domains) == 2 + #if is_multi_domain: + # return self.interdomain_client.LookUpSlice(request) + #else: + # raise NotImplementedError('Slice should create local services for single domain slice') + return self.create_update(request) @safe_and_metered_rpc_method(METRICS, LOGGER) - def DeleteSlice(self, request : TransportSlice, context : grpc.ServicerContext) -> Empty: + def DeleteSlice(self, request : SliceId, context : grpc.ServicerContext) -> Empty: return Empty() diff --git a/src/slice/service/__main__.py b/src/slice/service/__main__.py index 54bd059d2..76bb5fa34 100644 --- a/src/slice/service/__main__.py +++ b/src/slice/service/__main__.py @@ -15,8 +15,13 @@ import logging, signal, sys, threading from prometheus_client import start_http_server from common.Settings import get_setting, wait_for_environment_variables -#from common.database.Factory import get_database -from slice.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, LOG_LEVEL, METRICS_PORT +from context.client.ContextClient import ContextClient +from interdomain.client.InterdomainClient import InterdomainClient +from service.client.ServiceClient import ServiceClient +from slice.Config import ( + CONTEXT_SERVICE_HOST, CONTEXT_SERVICE_PORT, GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, + INTERDOMAIN_SERVICE_HOST, INTERDOMAIN_SERVICE_PORT, LOG_LEVEL, METRICS_PORT, SERVICE_SERVICE_HOST, + SERVICE_SERVICE_PORT) from .SliceService import SliceService terminate = threading.Event() @@ -29,24 +34,27 @@ def signal_handler(signal, frame): # pylint: disable=redefined-outer-name def main(): global LOGGER # pylint: disable=global-statement - grpc_service_port = get_setting('SLICESERVICE_SERVICE_PORT_GRPC', default=GRPC_SERVICE_PORT ) - max_workers = get_setting('MAX_WORKERS', default=GRPC_MAX_WORKERS ) - grace_period = get_setting('GRACE_PERIOD', default=GRPC_GRACE_PERIOD ) - log_level = get_setting('LOG_LEVEL', default=LOG_LEVEL ) - metrics_port = get_setting('METRICS_PORT', default=METRICS_PORT ) + grpc_service_port = get_setting('SLICESERVICE_SERVICE_PORT_GRPC', default=GRPC_SERVICE_PORT ) + max_workers = get_setting('MAX_WORKERS', default=GRPC_MAX_WORKERS ) + grace_period = get_setting('GRACE_PERIOD', default=GRPC_GRACE_PERIOD ) + log_level = get_setting('LOG_LEVEL', default=LOG_LEVEL ) + metrics_port = get_setting('METRICS_PORT', default=METRICS_PORT ) logging.basicConfig(level=log_level) LOGGER = logging.getLogger(__name__) wait_for_environment_variables([ - #'CONTEXTSERVICE_SERVICE_HOST', 'CONTEXTSERVICE_SERVICE_PORT_GRPC', - #'MONITORINGSERVICE_SERVICE_HOST', 'MONITORINGSERVICE_SERVICE_PORT_GRPC' + 'CONTEXTSERVICE_SERVICE_HOST', 'CONTEXTSERVICE_SERVICE_PORT_GRPC', + 'INTERDOMAINSERVICE_SERVICE_HOST', 'INTERDOMAINSERVICE_SERVICE_PORT_GRPC', + 'SERVICESERVICE_SERVICE_HOST', 'SERVICESERVICE_SERVICE_PORT_GRPC', ]) - #context_service_host = get_setting('CONTEXTSERVICE_SERVICE_HOST', default=CONTEXT_SERVICE_HOST ) - #context_service_port = get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC', default=CONTEXT_SERVICE_PORT ) - #monitoring_service_host = get_setting('MONITORINGSERVICE_SERVICE_HOST', default=MONITORING_SERVICE_HOST) - #monitoring_service_port = get_setting('MONITORINGSERVICE_SERVICE_PORT_GRPC', default=MONITORING_SERVICE_PORT) + context_service_host = get_setting('CONTEXTSERVICE_SERVICE_HOST', default=CONTEXT_SERVICE_HOST ) + context_service_port = get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC', default=CONTEXT_SERVICE_PORT ) + interdomain_service_host = get_setting('INTERDOMAINSERVICE_SERVICE_HOST', default=INTERDOMAIN_SERVICE_HOST) + interdomain_service_port = get_setting('INTERDOMAINSERVICE_SERVICE_PORT_GRPC', default=INTERDOMAIN_SERVICE_PORT) + service_service_host = get_setting('SERVICESERVICE_SERVICE_HOST', default=SERVICE_SERVICE_HOST ) + service_service_port = get_setting('SERVICESERVICE_SERVICE_PORT_GRPC', default=SERVICE_SERVICE_PORT ) signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) @@ -56,25 +64,28 @@ def main(): # Start metrics server start_http_server(metrics_port) - ## Initialize Context Client - #if context_service_host is None or context_service_port is None: - # raise Exception('Wrong address({:s}):port({:s}) of Context component'.format( - # str(context_service_host), str(context_service_port))) - #context_client = ContextClient(context_service_host, context_service_port) + # Initialize Context Client + if context_service_host is None or context_service_port is None: + raise Exception('Wrong address({:s}):port({:s}) of Context component'.format( + str(context_service_host), str(context_service_port))) + context_client = ContextClient(context_service_host, context_service_port) - ## Initialize Monitoring Client - #if monitoring_service_host is None or monitoring_service_port is None: - # raise Exception('Wrong address({:s}):port({:s}) of Monitoring component'.format( - # str(monitoring_service_host), str(monitoring_service_port))) - #monitoring_client = MonitoringClient(monitoring_service_host, monitoring_service_port) + # Initialize Interdomain Client + if interdomain_service_host is None or interdomain_service_port is None: + raise Exception('Wrong address({:s}):port({:s}) of Interdomain component'.format( + str(interdomain_service_host), str(interdomain_service_port))) + interdomain_client = InterdomainClient(interdomain_service_host, interdomain_service_port) - ## Get database instance - #database = get_database() + # Initialize Service Client + if service_service_host is None or service_service_port is None: + raise Exception('Wrong address({:s}):port({:s}) of Service component'.format( + str(service_service_host), str(service_service_port))) + service_client = ServiceClient(service_service_host, service_service_port) # Starting slice service grpc_service = SliceService( - #database, - port=grpc_service_port, max_workers=max_workers, grace_period=grace_period) + context_client, interdomain_client, service_client, port=grpc_service_port, max_workers=max_workers, + grace_period=grace_period) grpc_service.start() # Wait for Ctrl+C or termination signal diff --git a/src/tests/oeccpsc22/deploy_in_kubernetes.sh b/src/tests/oeccpsc22/deploy_in_kubernetes.sh index e350b8355..426e07e13 100755 --- a/src/tests/oeccpsc22/deploy_in_kubernetes.sh +++ b/src/tests/oeccpsc22/deploy_in_kubernetes.sh @@ -15,19 +15,145 @@ # OECC/PSC 22 deployment settings - export REGISTRY_IMAGE="" export COMPONENTS="context device monitoring service slice interdomain compute" # webui export IMAGE_TAG="oeccpsc22" export K8S_HOSTNAME="kubernetes-master" #export GRAFANA_PASSWORD="admin123+" -# Deploy TeraFlow instance 1 -export K8S_NAMESPACE="oeccpsc22-1" -export EXTRA_MANIFESTS="./oeccpsc22/expose_services_teraflow_1.yaml" -./deploy_in_kubernetes.sh +# Constants +GITLAB_REPO_URL="registry.gitlab.com/teraflow-h2020/controller" +TMP_FOLDER="./tmp" + +# Create a tmp folder for files modified during the deployment +TMP_MANIFESTS_FOLDER="$TMP_FOLDER/manifests" +mkdir -p $TMP_MANIFESTS_FOLDER +TMP_LOGS_FOLDER="$TMP_FOLDER/logs" +mkdir -p $TMP_LOGS_FOLDER + +export K8S_NAMESPACE_1="oeccpsc22-1" +export K8S_NAMESPACE_2="oeccpsc22-2" + +export EXTRA_MANIFESTS_1="./oeccpsc22/expose_services_teraflow_1.yaml" +export EXTRA_MANIFESTS_2="./oeccpsc22/expose_services_teraflow_2.yaml" + +echo "Deleting and Creating new namespaces..." +kubectl delete namespace $K8S_NAMESPACE_1 $K8S_NAMESPACE_2 +kubectl create namespace $K8S_NAMESPACE_1 +kubectl create namespace $K8S_NAMESPACE_2 +printf "\n" + +echo "Creating secrets for InfluxDB..." +#TODO: make sure to change this when having a production deployment +kubectl create secret generic influxdb-secrets --namespace=$K8S_NAMESPACE_1 --from-literal=INFLUXDB_DB="monitoring" --from-literal=INFLUXDB_ADMIN_USER="teraflow" --from-literal=INFLUXDB_ADMIN_PASSWORD="teraflow" --from-literal=INFLUXDB_HTTP_AUTH_ENABLED="True" +kubectl create secret generic monitoring-secrets --namespace=$K8S_NAMESPACE_1 --from-literal=INFLUXDB_DATABASE="monitoring" --from-literal=INFLUXDB_USER="teraflow" --from-literal=INFLUXDB_PASSWORD="teraflow" --from-literal=INFLUXDB_HOSTNAME="localhost" + +kubectl create secret generic influxdb-secrets --namespace=$K8S_NAMESPACE_2 --from-literal=INFLUXDB_DB="monitoring" --from-literal=INFLUXDB_ADMIN_USER="teraflow" --from-literal=INFLUXDB_ADMIN_PASSWORD="teraflow" --from-literal=INFLUXDB_HTTP_AUTH_ENABLED="True" +kubectl create secret generic monitoring-secrets --namespace=$K8S_NAMESPACE_2 --from-literal=INFLUXDB_DATABASE="monitoring" --from-literal=INFLUXDB_USER="teraflow" --from-literal=INFLUXDB_PASSWORD="teraflow" --from-literal=INFLUXDB_HOSTNAME="localhost" +printf "\n" + +echo "Pulling/Updating Docker images..." +docker pull redis:6.2 +docker pull influxdb:1.8 +docker pull grafana/grafana:8.2.6 +printf "\n" + +echo "Deploying components..." +for COMPONENT in $COMPONENTS; do + echo "Processing '$COMPONENT' component..." + IMAGE_NAME="$COMPONENT:$IMAGE_TAG" + IMAGE_URL="$REGISTRY_IMAGE/$IMAGE_NAME" + + echo " Building Docker image..." + BUILD_LOG="$TMP_LOGS_FOLDER/build_${COMPONENT}.log" + + if [ "$COMPONENT" == "automation" ] || [ "$COMPONENT" == "policy" ]; then + docker build -t "$IMAGE_NAME" -f ./src/"$COMPONENT"/Dockerfile ./src/"$COMPONENT"/ > "$BUILD_LOG" + else + docker build -t "$IMAGE_NAME" -f ./src/"$COMPONENT"/Dockerfile ./src/ > "$BUILD_LOG" + fi + + if [ -n "$REGISTRY_IMAGE" ]; then + echo "Pushing Docker image to '$REGISTRY_IMAGE'..." + + TAG_LOG="$TMP_LOGS_FOLDER/tag_${COMPONENT}.log" + docker tag "$IMAGE_NAME" "$IMAGE_URL" > "$TAG_LOG" + + PUSH_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}.log" + docker push "$IMAGE_URL" > "$PUSH_LOG" + fi + + echo " Adapting '$COMPONENT' manifest file..." + MANIFEST="$TMP_MANIFESTS_FOLDER/${COMPONENT}service.yaml" + cp ./manifests/"${COMPONENT}"service.yaml "$MANIFEST" + VERSION=$(grep -i "${GITLAB_REPO_URL}/${COMPONENT}:" "$MANIFEST" | cut -d ":" -f3) + + if [ -n "$REGISTRY_IMAGE" ]; then + + sed -E -i "s#image: $GITLAB_REPO_URL/$COMPONENT:${VERSION}#image: $IMAGE_URL#g" "$MANIFEST" + sed -E -i "s#imagePullPolicy: .*#imagePullPolicy: Always#g" "$MANIFEST" + + else + sed -E -i "s#image: $GITLAB_REPO_URL/$COMPONENT:${VERSION}#image: $IMAGE_NAME#g" "$MANIFEST" + sed -E -i "s#imagePullPolicy: .*#imagePullPolicy: Never#g" "$MANIFEST" + fi + + echo " Deploying '$COMPONENT' component to Kubernetes $K8S_NAMESPACE_1..." + DEPLOY_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}_1.log" + kubectl --namespace $K8S_NAMESPACE_1 apply -f "$MANIFEST" > "$DEPLOY_LOG" + kubectl --namespace $K8S_NAMESPACE_1 scale deployment --replicas=0 ${COMPONENT}service >> "$DEPLOY_LOG" + kubectl --namespace $K8S_NAMESPACE_1 scale deployment --replicas=1 ${COMPONENT}service >> "$DEPLOY_LOG" + + echo " Deploying '$COMPONENT' component to Kubernetes $K8S_NAMESPACE_2..." + DEPLOY_LOG="$TMP_LOGS_FOLDER/push_${COMPONENT}_2.log" + kubectl --namespace $K8S_NAMESPACE_2 apply -f "$MANIFEST" > "$DEPLOY_LOG" + kubectl --namespace $K8S_NAMESPACE_2 scale deployment --replicas=0 ${COMPONENT}service >> "$DEPLOY_LOG" + kubectl --namespace $K8S_NAMESPACE_2 scale deployment --replicas=1 ${COMPONENT}service >> "$DEPLOY_LOG" + + printf "\n" +done + +echo "Deploying extra manifests to Kubernetes $K8S_NAMESPACE_1..." +for EXTRA_MANIFEST in $EXTRA_MANIFESTS_1; do + echo "Processing manifest '$EXTRA_MANIFEST'..." + kubectl --namespace $K8S_NAMESPACE_1 apply -f $EXTRA_MANIFEST + printf "\n" +done + +echo "Deploying extra manifests to Kubernetes $K8S_NAMESPACE_2..." +for EXTRA_MANIFEST in $EXTRA_MANIFESTS_2; do + echo "Processing manifest '$EXTRA_MANIFEST'..." + kubectl --namespace $K8S_NAMESPACE_2 apply -f $EXTRA_MANIFEST + printf "\n" +done + +# By now, leave this control here. Some component dependencies are not well handled +for COMPONENT in $COMPONENTS; do + echo "Waiting for '$COMPONENT' component in Kubernetes $K8S_NAMESPACE_1..." + kubectl wait --namespace $K8S_NAMESPACE_1 --for='condition=available' --timeout=300s deployment/${COMPONENT}service + printf "\n" + + echo "Waiting for '$COMPONENT' component in Kubernetes $K8S_NAMESPACE_2..." + kubectl wait --namespace $K8S_NAMESPACE_2 --for='condition=available' --timeout=300s deployment/${COMPONENT}service + printf "\n" +done + +if [[ "$COMPONENTS" == *"webui"* ]]; then + echo "Configuring WebUI DataStores and Dashboards..." + ./configure_dashboards.sh + printf "\n\n" +fi + +echo "Removing dangling docker images..." +docker images --filter="dangling=true" --quiet | xargs -r docker rmi +printf "\n" + +echo "Reporting Deployment in Kubernetes $K8S_NAMESPACE_1..." +kubectl --namespace $K8S_NAMESPACE_1 get all +printf "\n" + +echo "Reporting Deployment in Kubernetes $K8S_NAMESPACE_2..." +kubectl --namespace $K8S_NAMESPACE_2 get all +printf "\n" -# Deploy TeraFlow instance 2 -export K8S_NAMESPACE="oeccpsc22-2" -export EXTRA_MANIFESTS="./oeccpsc22/expose_services_teraflow_2.yaml" -./deploy_in_kubernetes.sh +echo "Done!" diff --git a/src/tests/oeccpsc22/dump_logs.sh b/src/tests/oeccpsc22/dump_logs.sh new file mode 100755 index 000000000..c3c2e6284 --- /dev/null +++ b/src/tests/oeccpsc22/dump_logs.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +mkdir -p tmp/exec_logs/ + +kubectl --namespace oeccpsc22-1 logs deployment/computeservice -c server > tmp/exec_logs/d1_compute.log +kubectl --namespace oeccpsc22-1 logs deployment/contextservice -c server > tmp/exec_logs/d1_context.log +kubectl --namespace oeccpsc22-1 logs deployment/deviceservice -c server > tmp/exec_logs/d1_device.log +kubectl --namespace oeccpsc22-1 logs deployment/interdomainservice -c server > tmp/exec_logs/d1_interdomain.log +kubectl --namespace oeccpsc22-1 logs deployment/monitoringservice -c server > tmp/exec_logs/d1_monitoring.log +kubectl --namespace oeccpsc22-1 logs deployment/serviceservice -c server > tmp/exec_logs/d1_service.log +kubectl --namespace oeccpsc22-1 logs deployment/sliceservice -c server > tmp/exec_logs/d1_slice.log + +kubectl --namespace oeccpsc22-2 logs deployment/computeservice -c server > tmp/exec_logs/d2_compute.log +kubectl --namespace oeccpsc22-2 logs deployment/contextservice -c server > tmp/exec_logs/d2_context.log +kubectl --namespace oeccpsc22-2 logs deployment/deviceservice -c server > tmp/exec_logs/d2_device.log +kubectl --namespace oeccpsc22-2 logs deployment/interdomainservice -c server > tmp/exec_logs/d2_interdomain.log +kubectl --namespace oeccpsc22-2 logs deployment/monitoringservice -c server > tmp/exec_logs/d2_monitoring.log +kubectl --namespace oeccpsc22-2 logs deployment/serviceservice -c server > tmp/exec_logs/d2_service.log +kubectl --namespace oeccpsc22-2 logs deployment/sliceservice -c server > tmp/exec_logs/d2_slice.log diff --git a/src/tests/oeccpsc22/expose_services_teraflow_1.yaml b/src/tests/oeccpsc22/expose_services_teraflow_1.yaml index 6d2f0bed7..f2b8de0b1 100644 --- a/src/tests/oeccpsc22/expose_services_teraflow_1.yaml +++ b/src/tests/oeccpsc22/expose_services_teraflow_1.yaml @@ -40,6 +40,11 @@ spec: port: 1010 targetPort: 1010 nodePort: 30111 + - name: rest + protocol: TCP + port: 8080 + targetPort: 8080 + nodePort: 30001 - name: redis protocol: TCP port: 6379 diff --git a/src/tests/oeccpsc22/expose_services_teraflow_2.yaml b/src/tests/oeccpsc22/expose_services_teraflow_2.yaml index 32974848e..8254c00aa 100644 --- a/src/tests/oeccpsc22/expose_services_teraflow_2.yaml +++ b/src/tests/oeccpsc22/expose_services_teraflow_2.yaml @@ -18,7 +18,7 @@ metadata: name: remote-teraflow spec: type: ExternalName - externalName: interdomainservice.oeccpsc22-2.svc.cluster.local + externalName: interdomainservice.oeccpsc22-1.svc.cluster.local ports: - name: grpc protocol: TCP @@ -40,6 +40,11 @@ spec: port: 1010 targetPort: 1010 nodePort: 30112 + - name: rest + protocol: TCP + port: 8080 + targetPort: 8080 + nodePort: 30002 - name: redis protocol: TCP port: 6379 diff --git a/src/tests/oeccpsc22/tests/Objects_Domain_1.py b/src/tests/oeccpsc22/tests/Objects_Domain_1.py index 6fefc2d82..8b26348c9 100644 --- a/src/tests/oeccpsc22/tests/Objects_Domain_1.py +++ b/src/tests/oeccpsc22/tests/Objects_Domain_1.py @@ -40,38 +40,44 @@ D1_DEVICE_ENDPOINT_DEFS = [ ('3/5', '10Gbps', []), ('3/6', '10Gbps', []), ('3/7', '10Gbps', []), ('3/8', '10Gbps', []), ] -D1_DEVICE_D1R1_UUID = 'D1-R1' +D1_DEVICE_D1R1_UUID = 'R1@D1' D1_DEVICE_D1R1_ID = json_device_id(D1_DEVICE_D1R1_UUID) D1_DEVICE_D1R1 = json_device_emulated_packet_router_disabled(D1_DEVICE_D1R1_UUID) D1_DEVICE_D1R1_CONNECT_RULES = json_device_emulated_connect_rules(D1_DEVICE_ENDPOINT_DEFS) -D1_DEVICE_D1R2_UUID = 'D1-R2' +D1_DEVICE_D1R2_UUID = 'R2@D1' D1_DEVICE_D1R2_ID = json_device_id(D1_DEVICE_D1R2_UUID) D1_DEVICE_D1R2 = json_device_emulated_packet_router_disabled(D1_DEVICE_D1R2_UUID) D1_DEVICE_D1R2_CONNECT_RULES = json_device_emulated_connect_rules(D1_DEVICE_ENDPOINT_DEFS) -D1_DEVICE_D1R3_UUID = 'D1-R3' +D1_DEVICE_D1R3_UUID = 'R3@D1' D1_DEVICE_D1R3_ID = json_device_id(D1_DEVICE_D1R3_UUID) D1_DEVICE_D1R3 = json_device_emulated_packet_router_disabled(D1_DEVICE_D1R3_UUID) D1_DEVICE_D1R3_CONNECT_RULES = json_device_emulated_connect_rules(D1_DEVICE_ENDPOINT_DEFS) -D1_DEVICE_D1R4_UUID = 'D1-R4' +D1_DEVICE_D1R4_UUID = 'R4@D1' D1_DEVICE_D1R4_ID = json_device_id(D1_DEVICE_D1R4_UUID) D1_DEVICE_D1R4 = json_device_emulated_packet_router_disabled(D1_DEVICE_D1R4_UUID) D1_DEVICE_D1R4_CONNECT_RULES = json_device_emulated_connect_rules(D1_DEVICE_ENDPOINT_DEFS) # Virtual devices on remote domains -D1_DEVICE_D2R1_UUID = 'D2-R1' +D1_DEVICE_D2R1_UUID = 'R1@D2' D1_DEVICE_D2R1_ID = json_device_id(D1_DEVICE_D2R1_UUID) D1_DEVICE_D2R1 = json_device_emulated_packet_router_disabled(D1_DEVICE_D2R1_UUID) D1_DEVICE_D2R1_CONNECT_RULES = json_device_emulated_connect_rules(D1_DEVICE_ENDPOINT_DEFS) +D1_DEVICE_D2R4_UUID = 'R4@D2' +D1_DEVICE_D2R4_ID = json_device_id(D1_DEVICE_D2R4_UUID) +D1_DEVICE_D2R4 = json_device_emulated_packet_router_disabled(D1_DEVICE_D2R4_UUID) +D1_DEVICE_D2R4_CONNECT_RULES = json_device_emulated_connect_rules(D1_DEVICE_ENDPOINT_DEFS) + D1_ENDPOINT_IDS = {} D1_ENDPOINT_IDS.update(json_endpoint_ids(D1_DEVICE_D1R1_ID, D1_DEVICE_ENDPOINT_DEFS)) D1_ENDPOINT_IDS.update(json_endpoint_ids(D1_DEVICE_D1R2_ID, D1_DEVICE_ENDPOINT_DEFS)) D1_ENDPOINT_IDS.update(json_endpoint_ids(D1_DEVICE_D1R3_ID, D1_DEVICE_ENDPOINT_DEFS)) D1_ENDPOINT_IDS.update(json_endpoint_ids(D1_DEVICE_D1R4_ID, D1_DEVICE_ENDPOINT_DEFS)) D1_ENDPOINT_IDS.update(json_endpoint_ids(D1_DEVICE_D2R1_ID, D1_DEVICE_ENDPOINT_DEFS)) +D1_ENDPOINT_IDS.update(json_endpoint_ids(D1_DEVICE_D2R4_ID, D1_DEVICE_ENDPOINT_DEFS)) # ----- Links ---------------------------------------------------------------------------------------------------------- @@ -118,6 +124,7 @@ D1_DEVICES = [ (D1_DEVICE_D1R3, D1_DEVICE_D1R3_CONNECT_RULES), (D1_DEVICE_D1R4, D1_DEVICE_D1R4_CONNECT_RULES), (D1_DEVICE_D2R1, D1_DEVICE_D2R1_CONNECT_RULES), + (D1_DEVICE_D2R4, D1_DEVICE_D2R4_CONNECT_RULES), ] D1_LINKS = [ diff --git a/src/tests/oeccpsc22/tests/Objects_Domain_2.py b/src/tests/oeccpsc22/tests/Objects_Domain_2.py index ec641dce4..f91338092 100644 --- a/src/tests/oeccpsc22/tests/Objects_Domain_2.py +++ b/src/tests/oeccpsc22/tests/Objects_Domain_2.py @@ -40,38 +40,44 @@ D2_DEVICE_ENDPOINT_DEFS = [ ('3/5', '10Gbps', []), ('3/6', '10Gbps', []), ('3/7', '10Gbps', []), ('3/8', '10Gbps', []), ] -D2_DEVICE_D2R1_UUID = 'D2-R1' +D2_DEVICE_D2R1_UUID = 'R1@D2' D2_DEVICE_D2R1_ID = json_device_id(D2_DEVICE_D2R1_UUID) D2_DEVICE_D2R1 = json_device_emulated_packet_router_disabled(D2_DEVICE_D2R1_UUID) D2_DEVICE_D2R1_CONNECT_RULES = json_device_emulated_connect_rules(D2_DEVICE_ENDPOINT_DEFS) -D2_DEVICE_D2R2_UUID = 'D2-R2' +D2_DEVICE_D2R2_UUID = 'R2@D2' D2_DEVICE_D2R2_ID = json_device_id(D2_DEVICE_D2R2_UUID) D2_DEVICE_D2R2 = json_device_emulated_packet_router_disabled(D2_DEVICE_D2R2_UUID) D2_DEVICE_D2R2_CONNECT_RULES = json_device_emulated_connect_rules(D2_DEVICE_ENDPOINT_DEFS) -D2_DEVICE_D2R3_UUID = 'D2-R3' +D2_DEVICE_D2R3_UUID = 'R3@D2' D2_DEVICE_D2R3_ID = json_device_id(D2_DEVICE_D2R3_UUID) D2_DEVICE_D2R3 = json_device_emulated_packet_router_disabled(D2_DEVICE_D2R3_UUID) D2_DEVICE_D2R3_CONNECT_RULES = json_device_emulated_connect_rules(D2_DEVICE_ENDPOINT_DEFS) -D2_DEVICE_D2R4_UUID = 'D2-R4' +D2_DEVICE_D2R4_UUID = 'R4@D2' D2_DEVICE_D2R4_ID = json_device_id(D2_DEVICE_D2R4_UUID) D2_DEVICE_D2R4 = json_device_emulated_packet_router_disabled(D2_DEVICE_D2R4_UUID) D2_DEVICE_D2R4_CONNECT_RULES = json_device_emulated_connect_rules(D2_DEVICE_ENDPOINT_DEFS) # Virtual devices on remote domains -D2_DEVICE_D1R1_UUID = 'D1-R1' +D2_DEVICE_D1R1_UUID = 'R1@D1' D2_DEVICE_D1R1_ID = json_device_id(D2_DEVICE_D1R1_UUID) D2_DEVICE_D1R1 = json_device_emulated_packet_router_disabled(D2_DEVICE_D1R1_UUID) D2_DEVICE_D1R1_CONNECT_RULES = json_device_emulated_connect_rules(D2_DEVICE_ENDPOINT_DEFS) +D2_DEVICE_D1R4_UUID = 'R4@D1' +D2_DEVICE_D1R4_ID = json_device_id(D2_DEVICE_D1R4_UUID) +D2_DEVICE_D1R4 = json_device_emulated_packet_router_disabled(D2_DEVICE_D1R4_UUID) +D2_DEVICE_D1R4_CONNECT_RULES = json_device_emulated_connect_rules(D2_DEVICE_ENDPOINT_DEFS) + D2_ENDPOINT_IDS = {} D2_ENDPOINT_IDS.update(json_endpoint_ids(D2_DEVICE_D2R1_ID, D2_DEVICE_ENDPOINT_DEFS)) D2_ENDPOINT_IDS.update(json_endpoint_ids(D2_DEVICE_D2R2_ID, D2_DEVICE_ENDPOINT_DEFS)) D2_ENDPOINT_IDS.update(json_endpoint_ids(D2_DEVICE_D2R3_ID, D2_DEVICE_ENDPOINT_DEFS)) D2_ENDPOINT_IDS.update(json_endpoint_ids(D2_DEVICE_D2R4_ID, D2_DEVICE_ENDPOINT_DEFS)) D2_ENDPOINT_IDS.update(json_endpoint_ids(D2_DEVICE_D1R1_ID, D2_DEVICE_ENDPOINT_DEFS)) +D2_ENDPOINT_IDS.update(json_endpoint_ids(D2_DEVICE_D1R4_ID, D2_DEVICE_ENDPOINT_DEFS)) # ----- Links ---------------------------------------------------------------------------------------------------------- @@ -118,6 +124,7 @@ D2_DEVICES = [ (D2_DEVICE_D2R3, D2_DEVICE_D2R3_CONNECT_RULES), (D2_DEVICE_D2R4, D2_DEVICE_D2R4_CONNECT_RULES), (D2_DEVICE_D1R1, D2_DEVICE_D1R1_CONNECT_RULES), + (D2_DEVICE_D1R4, D2_DEVICE_D1R4_CONNECT_RULES), ] D2_LINKS = [ -- GitLab From 58251afe5abcf311a98f75f267b51b6074e58dfd Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Fri, 11 Mar 2022 19:31:37 +0100 Subject: [PATCH 020/353] Fixed Compute unit tests. - Added Mock Slice service - Corrected Compute unit tests --- src/common/tests/MockServicerImpl_Slice.py | 45 ++++++++++++++++++++++ src/compute/tests/test_unitary.py | 6 +++ 2 files changed, 51 insertions(+) create mode 100644 src/common/tests/MockServicerImpl_Slice.py diff --git a/src/common/tests/MockServicerImpl_Slice.py b/src/common/tests/MockServicerImpl_Slice.py new file mode 100644 index 000000000..5fd349ee1 --- /dev/null +++ b/src/common/tests/MockServicerImpl_Slice.py @@ -0,0 +1,45 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 grpc, logging +from common.Settings import get_setting +from common.tools.grpc.Tools import grpc_message_to_json_string +from context.client.ContextClient import ContextClient +from slice.proto.context_pb2 import Empty, Slice, SliceId, SliceStatusEnum +from slice.proto.slice_pb2_grpc import SliceServiceServicer + +LOGGER = logging.getLogger(__name__) + +class MockServicerImpl_Slice(SliceServiceServicer): + def __init__(self): + LOGGER.info('[__init__] Creating Servicer...') + self.context_client = ContextClient( + get_setting('CONTEXTSERVICE_SERVICE_HOST'), + get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) + LOGGER.info('[__init__] Servicer Created') + + def CreateSlice(self, request : Slice, context : grpc.ServicerContext) -> SliceId: + LOGGER.info('[CreateSlice] request={:s}'.format(grpc_message_to_json_string(request))) + return self.context_client.SetSlice(request) + + def UpdateSlice(self, request : Slice, context : grpc.ServicerContext) -> SliceId: + LOGGER.info('[UpdateSlice] request={:s}'.format(grpc_message_to_json_string(request))) + slice_ = Slice() + slice_.CopyFrom(request) + slice_.slice_status.slice_status = SliceStatusEnum.SLICESTATUS_ACTIVE # pylint: disable=no-member + return self.context_client.SetSlice(slice_) + + def DeleteSlice(self, request : SliceId, context : grpc.ServicerContext) -> Empty: + LOGGER.info('[DeleteSlice] request={:s}'.format(grpc_message_to_json_string(request))) + return self.context_client.RemoveSlice(request) diff --git a/src/compute/tests/test_unitary.py b/src/compute/tests/test_unitary.py index 2fa586986..1fbc74ecc 100644 --- a/src/compute/tests/test_unitary.py +++ b/src/compute/tests/test_unitary.py @@ -16,10 +16,12 @@ import logging, os, pytest, time from common.tests.MockService import MockService from common.tests.MockServicerImpl_Context import MockServicerImpl_Context from common.tests.MockServicerImpl_Service import MockServicerImpl_Service +from common.tests.MockServicerImpl_Slice import MockServicerImpl_Slice from compute.Config import RESTAPI_SERVICE_PORT, RESTAPI_BASE_URL from compute.service.rest_server.RestServer import RestServer from context.proto.context_pb2_grpc import add_ContextServiceServicer_to_server from service.proto.service_pb2_grpc import add_ServiceServiceServicer_to_server +from slice.proto.slice_pb2_grpc import add_SliceServiceServicer_to_server from .mock_osm.MockOSM import MockOSM from .Constants import ( SERVICE_CONNECTION_POINTS_1, SERVICE_CONNECTION_POINTS_2, SERVICE_TYPE, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD) @@ -43,11 +45,15 @@ class MockService_ContextService(MockService): add_ContextServiceServicer_to_server(self.context_servicer, self.server) self.service_servicer = MockServicerImpl_Service() add_ServiceServiceServicer_to_server(self.service_servicer, self.server) + self.slice_servicer = MockServicerImpl_Slice() + add_SliceServiceServicer_to_server(self.slice_servicer, self.server) os.environ['CONTEXTSERVICE_SERVICE_HOST'] = LOCALHOST os.environ['CONTEXTSERVICE_SERVICE_PORT_GRPC'] = str(MOCKSERVER_GRPC_PORT) os.environ['SERVICESERVICE_SERVICE_HOST'] = LOCALHOST os.environ['SERVICESERVICE_SERVICE_PORT_GRPC'] = str(MOCKSERVER_GRPC_PORT) +os.environ['SLICESERVICE_SERVICE_HOST'] = LOCALHOST +os.environ['SLICESERVICE_SERVICE_PORT_GRPC'] = str(MOCKSERVER_GRPC_PORT) # NBI Plugin IETF L2VPN requires environment variables CONTEXTSERVICE_SERVICE_HOST, CONTEXTSERVICE_SERVICE_PORT_GRPC, # SERVICESERVICE_SERVICE_HOST, and SERVICESERVICE_SERVICE_PORT_GRPC to work properly. -- GitLab From 547bb133fc852910ed00ec725875ac2d23d71038 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Mon, 2 May 2022 19:28:00 +0200 Subject: [PATCH 021/353] Monitoring component changes to fix unitary tests --- src/monitoring/requirements.in | 2 +- src/monitoring/tests/test_unitary.py | 25 ++++++++++++++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/monitoring/requirements.in b/src/monitoring/requirements.in index df2f1cf89..839ea33af 100644 --- a/src/monitoring/requirements.in +++ b/src/monitoring/requirements.in @@ -10,7 +10,7 @@ protobuf==3.19.3 pytest==6.2.5 pytest-benchmark==3.4.1 influxdb -#redis==4.1.2 +redis==4.1.2 #anytree==2.8.0 #APScheduler==3.8.1 #xmltodict==0.12.0 diff --git a/src/monitoring/tests/test_unitary.py b/src/monitoring/tests/test_unitary.py index 97cdf4890..0701c5ce8 100644 --- a/src/monitoring/tests/test_unitary.py +++ b/src/monitoring/tests/test_unitary.py @@ -19,10 +19,9 @@ import pytest from typing import Tuple -from monitoring.proto import context_pb2, kpi_sample_types_pb2 -from monitoring.proto import monitoring_pb2 from monitoring.client.monitoring_client import MonitoringClient -from monitoring.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, DEVICE_GRPC_GRACE_PERIOD, DEVICE_GRPC_MAX_WORKERS, DEVICE_GRPC_SERVICE_PORT, DEVICE_SERVICE_HOST +from monitoring.Config import GRPC_SERVICE_PORT, GRPC_MAX_WORKERS, GRPC_GRACE_PERIOD, DEVICE_GRPC_SERVICE_PORT +from monitoring.proto import context_pb2, monitoring_pb2 from monitoring.proto.kpi_sample_types_pb2 import KpiSampleType from monitoring.service import SqliteTools, InfluxTools from monitoring.service.MonitoringService import MonitoringService @@ -33,7 +32,11 @@ from common.orm.Factory import get_database_backend, BackendEnum as DatabaseBack from common.message_broker.Factory import get_messagebroker_backend, BackendEnum as MessageBrokerBackendEnum from common.message_broker.MessageBroker import MessageBroker -from context.Config import GRPC_SERVICE_PORT as grpc_port_context, GRPC_MAX_WORKERS as grpc_workers_context, GRPC_GRACE_PERIOD as grpc_grace_context +from context.Config import ( + GRPC_SERVICE_PORT as grpc_port_context, + GRPC_MAX_WORKERS as grpc_workers_context, + GRPC_GRACE_PERIOD as grpc_grace_context +) from context.client.ContextClient import ContextClient from context.service.grpc_server.ContextService import ContextService from context.service.Populate import populate @@ -104,15 +107,15 @@ def monitoring_service(): grace_period = GRPC_GRACE_PERIOD LOGGER.info('Initializing MonitoringService...') - grpc_service = MonitoringService(port=service_port, max_workers=max_workers, grace_period=grace_period) - server = grpc_service.start() + _service = MonitoringService(port=service_port, max_workers=max_workers, grace_period=grace_period) + _service.start() # yield the server, when test finishes, execution will resume to stop it LOGGER.warning('monitoring_service yielding') - yield server + yield _service LOGGER.info('Terminating MonitoringService...') - grpc_service.stop() + _service.stop() # This fixture will be requested by test cases and last during testing session. # The client requires the server, so client fixture has the server as dependency. @@ -159,7 +162,7 @@ def create_kpi_request(): create_kpi_request = monitoring_pb2.KpiDescriptor() create_kpi_request.kpi_description = 'KPI Description Test' - create_kpi_request.kpi_sample_type = kpi_sample_types_pb2.KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED + create_kpi_request.kpi_sample_type = KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED create_kpi_request.device_id.device_uuid.uuid = 'DEV1' # pylint: disable=maybe-no-member create_kpi_request.service_id.service_uuid.uuid = "SERV1" create_kpi_request.endpoint_id.endpoint_uuid.uuid = "END1" @@ -293,7 +296,7 @@ def test_sqlitedb_tools_get_kpis(sql_db): def test_sqlitedb_tools_delete_kpi(sql_db, create_kpi_request): LOGGER.warning('test_sqlitedb_tools_get_kpi begin') - response = sql_db.delete_KPI("DEV1",kpi_sample_types_pb2.KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED) + response = sql_db.delete_KPI("DEV1",KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED) if response == False: kpi_description = create_kpi_request.kpi_description @@ -303,7 +306,7 @@ def test_sqlitedb_tools_delete_kpi(sql_db, create_kpi_request): kpi_service_id = create_kpi_request.service_id.service_uuid.uuid sql_db.insert_KPI(kpi_description, kpi_sample_type, kpi_device_id, kpi_endpoint_id, kpi_service_id) - response = sql_db.delete_KPI("DEV1", kpi_sample_types_pb2.KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED) + response = sql_db.delete_KPI("DEV1", KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED) assert response == True -- GitLab From 6009325aaa0eb38d2cccfac7c3d07132ca69089f Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 11:28:59 +0200 Subject: [PATCH 022/353] Corrected liveness prove in manifest of WebUI --- manifests/webuiservice.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/manifests/webuiservice.yaml b/manifests/webuiservice.yaml index be7ad73d7..8c7c3773c 100644 --- a/manifests/webuiservice.yaml +++ b/manifests/webuiservice.yaml @@ -71,14 +71,14 @@ spec: path: /robots.txt port: 3000 scheme: HTTP - initialDelaySeconds: 5 - periodSeconds: 5 + initialDelaySeconds: 10 + periodSeconds: 30 successThreshold: 1 timeoutSeconds: 2 livenessProbe: failureThreshold: 3 - initialDelaySeconds: 5 - periodSeconds: 5 + initialDelaySeconds: 30 + periodSeconds: 10 successThreshold: 1 tcpSocket: port: 3000 -- GitLab From 2c2c8a476cafa06b85407a7db4b431aeb5514b2f Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 11:32:21 +0200 Subject: [PATCH 023/353] Compute: - fixed bug with slice retrieval - code cleanup --- .../rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py index 440b706ab..27489410f 100644 --- a/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py +++ b/src/compute/service/rest_server/nbi_plugins/ietf_l2vpn/L2VPN_Service.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from ctypes import Union import logging from flask import request from flask.json import jsonify @@ -20,7 +19,7 @@ from flask_restful import Resource from common.Constants import DEFAULT_CONTEXT_UUID from common.Settings import get_setting from context.client.ContextClient import ContextClient -from context.proto.context_pb2 import Service, ServiceId, Slice, SliceStatusEnum +from context.proto.context_pb2 import ServiceId, SliceStatusEnum from service.client.ServiceClient import ServiceClient from service.proto.context_pb2 import ServiceStatusEnum from .tools.Authentication import HTTP_AUTH @@ -50,16 +49,16 @@ class L2VPN_Service(Resource): if target.service_id.service_uuid.uuid != vpn_id: # pylint: disable=no-member raise Exception('Service retrieval failed. Wrong Service Id was returned') service_ready_status = ServiceStatusEnum.SERVICESTATUS_ACTIVE - service_status = target.service_status.service_status + service_status = target.service_status.service_status # pylint: disable=no-member response.status_code = HTTP_OK if service_status == service_ready_status else HTTP_GATEWAYTIMEOUT return response target = get_slice(self.context_client, vpn_id) - if target is None: + if target is not None: if target.slice_id.slice_uuid.uuid != vpn_id: # pylint: disable=no-member raise Exception('Slice retrieval failed. Wrong Slice Id was returned') slice_ready_status = SliceStatusEnum.SLICESTATUS_ACTIVE - slice_status = target.slice_status.slice_status + slice_status = target.slice_status.slice_status # pylint: disable=no-member response.status_code = HTTP_OK if slice_status == slice_ready_status else HTTP_GATEWAYTIMEOUT return response -- GitLab From 1e9344f06abd88e632f9ac16424cce385815b7fa Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 11:33:12 +0200 Subject: [PATCH 024/353] Context: - implemented retrieval of Slices through REST-API --- src/context/service/rest_server/Resources.py | 26 ++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/context/service/rest_server/Resources.py b/src/context/service/rest_server/Resources.py index 966c62f96..95c06d83c 100644 --- a/src/context/service/rest_server/Resources.py +++ b/src/context/service/rest_server/Resources.py @@ -17,7 +17,7 @@ from flask.json import jsonify from flask_restful import Resource from common.orm.Database import Database from common.tools.grpc.Tools import grpc_message_to_json -from context.proto.context_pb2 import ConnectionId, ContextId, DeviceId, Empty, LinkId, ServiceId, TopologyId +from context.proto.context_pb2 import ConnectionId, ContextId, DeviceId, Empty, LinkId, ServiceId, SliceId, TopologyId from context.service.grpc_server.ContextServiceServicerImpl import ContextServiceServicerImpl def format_grpc_to_json(grpc_reply): @@ -49,6 +49,12 @@ def grpc_service_id(context_uuid, service_uuid): 'service_uuid': {'uuid': service_uuid} }) +def grpc_slice_id(context_uuid, slice_uuid): + return SliceId(**{ + 'context_id': {'context_uuid': {'uuid': context_uuid}}, + 'slice_uuid': {'uuid': slice_uuid} + }) + def grpc_topology_id(context_uuid, topology_uuid): return TopologyId(**{ 'context_id': {'context_uuid': {'uuid': context_uuid}}, @@ -97,6 +103,18 @@ class Service(_Resource): def get(self, context_uuid : str, service_uuid : str): return format_grpc_to_json(self.servicer.GetService(grpc_service_id(context_uuid, service_uuid), None)) +class SliceIds(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.servicer.ListSliceIds(grpc_context_id(context_uuid), None)) + +class Slices(_Resource): + def get(self, context_uuid : str): + return format_grpc_to_json(self.servicer.ListSlices(grpc_context_id(context_uuid), None)) + +class Slice(_Resource): + def get(self, context_uuid : str, slice_uuid : str): + return format_grpc_to_json(self.servicer.GetSlice(grpc_slice_id(context_uuid, slice_uuid), None)) + class DeviceIds(_Resource): def get(self): return format_grpc_to_json(self.servicer.ListDeviceIds(Empty(), None)) @@ -141,7 +159,7 @@ class DumpText(Resource): def get(self): db_entries = self.database.dump() num_entries = len(db_entries) - response = ['----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))] + response = ['----- Database Dump [{:3d} entries] -------------------------'.format(num_entries)] for db_entry in db_entries: response.append(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover response.append('-----------------------------------------------------------') @@ -185,6 +203,10 @@ RESOURCES = [ ('api.services', Services, '/context//services'), ('api.service', Service, '/context//service/'), + ('api.slice_ids', SliceIds, '/context//slice_ids'), + ('api.slices', Slices, '/context//slices'), + ('api.slice', Slice, '/context//slice/'), + ('api.device_ids', DeviceIds, '/device_ids'), ('api.devices', Devices, '/devices'), ('api.device', Device, '/device/'), -- GitLab From f7a7c4143023f15bc41771850741e477d22bcc1d Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 11:34:28 +0200 Subject: [PATCH 025/353] Slice: - moved old code to a separate folder (temporary, until confirming it is useless) - minor code clean-up --- src/slice/{service => old_code}/Tools.py | 0 .../{_docs/old_conflicting_code.txt => old_code/Tools_2.py} | 0 .../tools => slice/old_code}/service/ConstraintsChecker.py | 0 src/{common/tools => slice/old_code}/service/SliceCheckers.py | 0 src/slice/service/SliceServiceServicerImpl.py | 1 - 5 files changed, 1 deletion(-) rename src/slice/{service => old_code}/Tools.py (100%) rename src/slice/{_docs/old_conflicting_code.txt => old_code/Tools_2.py} (100%) rename src/{common/tools => slice/old_code}/service/ConstraintsChecker.py (100%) rename src/{common/tools => slice/old_code}/service/SliceCheckers.py (100%) diff --git a/src/slice/service/Tools.py b/src/slice/old_code/Tools.py similarity index 100% rename from src/slice/service/Tools.py rename to src/slice/old_code/Tools.py diff --git a/src/slice/_docs/old_conflicting_code.txt b/src/slice/old_code/Tools_2.py similarity index 100% rename from src/slice/_docs/old_conflicting_code.txt rename to src/slice/old_code/Tools_2.py diff --git a/src/common/tools/service/ConstraintsChecker.py b/src/slice/old_code/service/ConstraintsChecker.py similarity index 100% rename from src/common/tools/service/ConstraintsChecker.py rename to src/slice/old_code/service/ConstraintsChecker.py diff --git a/src/common/tools/service/SliceCheckers.py b/src/slice/old_code/service/SliceCheckers.py similarity index 100% rename from src/common/tools/service/SliceCheckers.py rename to src/slice/old_code/service/SliceCheckers.py diff --git a/src/slice/service/SliceServiceServicerImpl.py b/src/slice/service/SliceServiceServicerImpl.py index dcc93622a..bd26d1943 100644 --- a/src/slice/service/SliceServiceServicerImpl.py +++ b/src/slice/service/SliceServiceServicerImpl.py @@ -13,7 +13,6 @@ # limitations under the License. import grpc, json, logging -from typing import Dict from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method from context.client.ContextClient import ContextClient from context.proto.context_pb2 import ( -- GitLab From f393ec3ef37dc59de2c885ed35fde6e0db4c54cb Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 11:36:17 +0200 Subject: [PATCH 026/353] OECC/PSC'22 Functional test: - renamed tests: create/delete service to create/delete interdomain slice - added missing checks over slices - tests are still incomplete --- ...> run_test_02_create_interdomain_slice.sh} | 2 +- ...> run_test_03_delete_interdomain_slice.sh} | 2 +- .../tests/test_functional_bootstrap.py | 9 ++ ...st_functional_create_interdomain_slice.py} | 26 ++-- ...est_functional_delete_interdomain_slice.py | 143 ++++++++++++++++++ .../tests/test_functional_delete_service.py | 134 ---------------- 6 files changed, 169 insertions(+), 147 deletions(-) rename src/tests/oeccpsc22/{run_test_02_create_service.sh => run_test_02_create_interdomain_slice.sh} (97%) rename src/tests/oeccpsc22/{run_test_03_delete_service.sh => run_test_03_delete_interdomain_slice.sh} (97%) rename src/tests/oeccpsc22/tests/{test_functional_create_service.py => test_functional_create_interdomain_slice.py} (85%) create mode 100644 src/tests/oeccpsc22/tests/test_functional_delete_interdomain_slice.py delete mode 100644 src/tests/oeccpsc22/tests/test_functional_delete_service.py diff --git a/src/tests/oeccpsc22/run_test_02_create_service.sh b/src/tests/oeccpsc22/run_test_02_create_interdomain_slice.sh similarity index 97% rename from src/tests/oeccpsc22/run_test_02_create_service.sh rename to src/tests/oeccpsc22/run_test_02_create_interdomain_slice.sh index 5101336ba..b9ab66cb3 100755 --- a/src/tests/oeccpsc22/run_test_02_create_service.sh +++ b/src/tests/oeccpsc22/run_test_02_create_interdomain_slice.sh @@ -46,4 +46,4 @@ export D2_COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice- # Run functional test and analyze coverage of code at same time coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ - tests/oeccpsc22/tests/test_functional_create_service.py + tests/oeccpsc22/tests/test_functional_create_interdomain_slice.py diff --git a/src/tests/oeccpsc22/run_test_03_delete_service.sh b/src/tests/oeccpsc22/run_test_03_delete_interdomain_slice.sh similarity index 97% rename from src/tests/oeccpsc22/run_test_03_delete_service.sh rename to src/tests/oeccpsc22/run_test_03_delete_interdomain_slice.sh index e8549a370..b0080ce37 100755 --- a/src/tests/oeccpsc22/run_test_03_delete_service.sh +++ b/src/tests/oeccpsc22/run_test_03_delete_interdomain_slice.sh @@ -46,4 +46,4 @@ export D2_COMPUTESERVICE_SERVICE_PORT_HTTP=$(kubectl get service computeservice- # Run functional test and analyze coverage of code at same time coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ - tests/oeccpsc22/tests/test_functional_delete_service.py + tests/oeccpsc22/tests/test_functional_delete_interdomain_slice.py diff --git a/src/tests/oeccpsc22/tests/test_functional_bootstrap.py b/src/tests/oeccpsc22/tests/test_functional_bootstrap.py index 413baa57e..452394c01 100644 --- a/src/tests/oeccpsc22/tests/test_functional_bootstrap.py +++ b/src/tests/oeccpsc22/tests/test_functional_bootstrap.py @@ -112,6 +112,9 @@ def test_scenario_ready( response = context_client.ListLinks(Empty()) assert len(response.links) == 0 + response = context_client.ListSlices(ContextId(**context_id)) + assert len(response.slices) == 0 + response = context_client.ListServices(ContextId(**context_id)) assert len(response.services) == 0 @@ -155,6 +158,9 @@ def test_devices_bootstrapped( response = context_client.ListLinks(Empty()) assert len(response.links) == 0 + response = context_client.ListSlices(ContextId(**context_id)) + assert len(response.slices) == 0 + response = context_client.ListServices(ContextId(**context_id)) assert len(response.services) == 0 @@ -196,6 +202,9 @@ def test_links_created( response = context_client.ListLinks(Empty()) assert len(response.links) == len(links) + response = context_client.ListSlices(ContextId(**context_id)) + assert len(response.slices) == 0 + response = context_client.ListServices(ContextId(**context_id)) assert len(response.services) == 0 diff --git a/src/tests/oeccpsc22/tests/test_functional_create_service.py b/src/tests/oeccpsc22/tests/test_functional_create_interdomain_slice.py similarity index 85% rename from src/tests/oeccpsc22/tests/test_functional_create_service.py rename to src/tests/oeccpsc22/tests/test_functional_create_interdomain_slice.py index ea9ffad70..e6f6ccbc9 100644 --- a/src/tests/oeccpsc22/tests/test_functional_create_service.py +++ b/src/tests/oeccpsc22/tests/test_functional_create_interdomain_slice.py @@ -19,14 +19,8 @@ from common.tools.grpc.Tools import grpc_message_to_json_string from compute.tests.mock_osm.MockOSM import MockOSM from context.client.ContextClient import ContextClient from context.proto.context_pb2 import ContextId, Empty -from tests.oeccpsc22.tests.Objects_Domain_1 import D1_CONTEXT_ID, D1_CONTEXTS, D1_DEVICES, D1_LINKS, D1_TOPOLOGIES -from tests.oeccpsc22.tests.Objects_Domain_2 import D2_CONTEXT_ID, D2_CONTEXTS, D2_DEVICES, D2_LINKS, D2_TOPOLOGIES -#from .Objects_Domain_1 import ( -# CONTEXT_ID, CONTEXTS, DEVICE_O1_UUID, DEVICE_R1_UUID, DEVICE_R3_UUID, DEVICES, LINKS, TOPOLOGIES, -# WIM_MAPPING, WIM_PASSWORD, WIM_SERVICE_CONNECTION_POINTS, WIM_SERVICE_TYPE, WIM_USERNAME) -#from .Objects_Domain_2 import ( -# CONTEXT_ID, CONTEXTS, DEVICE_O1_UUID, DEVICE_R1_UUID, DEVICE_R3_UUID, DEVICES, LINKS, TOPOLOGIES, -# WIM_MAPPING, WIM_PASSWORD, WIM_SERVICE_CONNECTION_POINTS, WIM_SERVICE_TYPE, WIM_USERNAME) +from .Objects_Domain_1 import D1_CONTEXT_ID, D1_CONTEXTS, D1_DEVICES, D1_LINKS, D1_TOPOLOGIES +from .Objects_Domain_2 import D2_CONTEXT_ID, D2_CONTEXTS, D2_DEVICES, D2_LINKS, D2_TOPOLOGIES from .Objects_Service import WIM_MAPPING, WIM_PASSWORD, WIM_SERVICE_CONNECTION_POINTS, WIM_SERVICE_TYPE, WIM_USERNAME LOGGER = logging.getLogger(__name__) @@ -76,6 +70,9 @@ def test_scenario_is_correct( response = context_client.ListLinks(Empty()) assert len(response.links) == len(links) + response = context_client.ListSlices(ContextId(**context_id)) + assert len(response.slices) == 0 + response = context_client.ListServices(ContextId(**context_id)) assert len(response.services) == 0 @@ -84,15 +81,15 @@ def test_scenario_is_correct( per_domain(D2_CONTEXTS, D2_TOPOLOGIES, D2_DEVICES, D2_LINKS, D2_CONTEXT_ID, d2_context_client) -def test_service_creation( +def test_interdomain_slice_creation( d1_osm_wim : MockOSM): # pylint: disable=redefined-outer-name - # ----- Create Service --------------------------------------------------------------------------------------------- + # ----- Create Inter-domain Slice ---------------------------------------------------------------------------------- service_uuid = d1_osm_wim.create_connectivity_service(WIM_SERVICE_TYPE, WIM_SERVICE_CONNECTION_POINTS) d1_osm_wim.get_connectivity_service_status(service_uuid) -def test_scenario_service_created( +def test_scenario_interdomain_slice_created( d1_context_client : ContextClient, # pylint: disable=redefined-outer-name d2_context_client : ContextClient): # pylint: disable=redefined-outer-name @@ -109,9 +106,16 @@ def test_scenario_service_created( response = context_client.ListLinks(Empty()) assert len(response.links) == len(links) + # TODO: implement validation of number of slices according to the domain they belong + #response = context_client.ListSlices(ContextId(**context_id)) + #LOGGER.info('Slices[{:d}] = {:s}'.format(len(response.slices), grpc_message_to_json_string(response))) + #assert len(response.slices) == (2 if D1 else 1) + # TODO: implement validation that slices are correct + response = context_client.ListServices(ContextId(**context_id)) LOGGER.info('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) assert len(response.services) == 1 # L3NM + # TODO: improve validation of services created for service in response.services: service_id = service.service_id response = context_client.ListConnections(service_id) diff --git a/src/tests/oeccpsc22/tests/test_functional_delete_interdomain_slice.py b/src/tests/oeccpsc22/tests/test_functional_delete_interdomain_slice.py new file mode 100644 index 000000000..2830225be --- /dev/null +++ b/src/tests/oeccpsc22/tests/test_functional_delete_interdomain_slice.py @@ -0,0 +1,143 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, pytest +from common.DeviceTypes import DeviceTypeEnum +from common.Settings import get_setting +from common.tools.grpc.Tools import grpc_message_to_json_string +from compute.tests.mock_osm.MockOSM import MockOSM +from context.client.ContextClient import ContextClient +from context.proto.context_pb2 import ContextId, Empty +from .Objects_Domain_1 import D1_CONTEXT_ID, D1_CONTEXTS, D1_DEVICES, D1_LINKS, D1_TOPOLOGIES +from .Objects_Domain_2 import D2_CONTEXT_ID, D2_CONTEXTS, D2_DEVICES, D2_LINKS, D2_TOPOLOGIES +from .Objects_Service import WIM_MAPPING, WIM_PASSWORD, WIM_USERNAME + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +DEVTYPE_EMU_PR = DeviceTypeEnum.EMULATED_PACKET_ROUTER.value +DEVTYPE_EMU_OLS = DeviceTypeEnum.EMULATED_OPTICAL_LINE_SYSTEM.value + + +@pytest.fixture(scope='session') +def d1_context_client(): + _client = ContextClient( + get_setting('D1_CONTEXTSERVICE_SERVICE_HOST'), get_setting('D1_CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +@pytest.fixture(scope='session') +def d2_context_client(): + _client = ContextClient( + get_setting('D2_CONTEXTSERVICE_SERVICE_HOST'), get_setting('D2_CONTEXTSERVICE_SERVICE_PORT_GRPC')) + yield _client + _client.close() + + +@pytest.fixture(scope='session') +def d1_osm_wim(): + wim_url = 'http://{:s}:{:s}'.format( + get_setting('D1_COMPUTESERVICE_SERVICE_HOST'), str(get_setting('D1_COMPUTESERVICE_SERVICE_PORT_HTTP'))) + return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD) + + +def test_interdomain_slice_created( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(contexts, topologies, devices, links, context_id, context_client): + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(contexts) + + response = context_client.ListTopologies(ContextId(**context_id)) + assert len(response.topologies) == len(topologies) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(devices) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(links) + + # TODO: implement validation of number of slices according to the domain they belong + #response = context_client.ListSlices(ContextId(**context_id)) + #LOGGER.info('Slices[{:d}] = {:s}'.format(len(response.slices), grpc_message_to_json_string(response))) + #assert len(response.slices) == (2 if D1 else 1) + # TODO: implement validation that slices are correct + + response = context_client.ListServices(ContextId(**context_id)) + LOGGER.info('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) + assert len(response.services) == 1 # L3NM + # TODO: improve validation of services created + for service in response.services: + service_id = service.service_id + response = context_client.ListConnections(service_id) + LOGGER.info(' ServiceId[{:s}] => Connections[{:d}] = {:s}'.format( + grpc_message_to_json_string(service_id), len(response.connections), + grpc_message_to_json_string(response))) + assert len(response.connections) == 1 # one connection per service + + # ----- List entities - Ensure Inter-domain slice is created ------------------------------------------------------- + per_domain(D1_CONTEXTS, D1_TOPOLOGIES, D1_DEVICES, D1_LINKS, D1_CONTEXT_ID, d1_context_client) + per_domain(D2_CONTEXTS, D2_TOPOLOGIES, D2_DEVICES, D2_LINKS, D2_CONTEXT_ID, d2_context_client) + + +def test_interdomain_slice_removal( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d1_osm_wim : MockOSM): # pylint: disable=redefined-outer-name + + # ----- Remove Inter-domain Slice ---------------------------------------------------------------------------------- + # TODO: implement retrieval of inter-domain slice to be deleted + #response = d1_context_client.ListSliceIds(ContextId(**D1_CONTEXT_ID)) + #LOGGER.info('SliceIds[{:d}] = {:s}'.format(len(response.slice_ids), grpc_message_to_json_string(response))) + #assert len(response.service_ids) == 2 # L3NM + TAPI + #service_uuids = set() + #for service_id in response.service_ids: + # service_uuid = service_id.service_uuid.uuid + # if service_uuid.endswith(':optical'): continue + # service_uuids.add(service_uuid) + # osm_wim.conn_info[service_uuid] = {} + + # TODO: implement removal of service + #assert len(service_uuids) == 1 # assume a single service has been created + #service_uuid = set(service_uuids).pop() + #osm_wim.delete_connectivity_service(service_uuid) + + +def test_interdomain_slice_removed( + d1_context_client : ContextClient, # pylint: disable=redefined-outer-name + d2_context_client : ContextClient): # pylint: disable=redefined-outer-name + + def per_domain(contexts, topologies, devices, links, context_id, context_client): + response = context_client.ListContexts(Empty()) + assert len(response.contexts) == len(contexts) + + response = context_client.ListTopologies(ContextId(**context_id)) + assert len(response.topologies) == len(topologies) + + response = context_client.ListDevices(Empty()) + assert len(response.devices) == len(devices) + + response = context_client.ListLinks(Empty()) + assert len(response.links) == len(links) + + response = context_client.ListSlices(ContextId(**context_id)) + assert len(response.slices) == 0 + + response = context_client.ListServices(ContextId(**context_id)) + assert len(response.services) == 0 + + # ----- List entities - Ensure services removed -------------------------------------------------------------------- + per_domain(D1_CONTEXTS, D1_TOPOLOGIES, D1_DEVICES, D1_LINKS, D1_CONTEXT_ID, d1_context_client) + per_domain(D2_CONTEXTS, D2_TOPOLOGIES, D2_DEVICES, D2_LINKS, D2_CONTEXT_ID, d2_context_client) diff --git a/src/tests/oeccpsc22/tests/test_functional_delete_service.py b/src/tests/oeccpsc22/tests/test_functional_delete_service.py deleted file mode 100644 index 51e91a596..000000000 --- a/src/tests/oeccpsc22/tests/test_functional_delete_service.py +++ /dev/null @@ -1,134 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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 logging, pytest -from common.DeviceTypes import DeviceTypeEnum -from common.Settings import get_setting -from common.tests.EventTools import EVENT_REMOVE, EVENT_UPDATE, check_events -from common.tools.object_factory.Connection import json_connection_id -from common.tools.object_factory.Device import json_device_id -from common.tools.object_factory.Service import json_service_id -from common.tools.grpc.Tools import grpc_message_to_json_string -from compute.tests.mock_osm.MockOSM import MockOSM -from context.client.ContextClient import ContextClient -from context.client.EventsCollector import EventsCollector -from context.proto.context_pb2 import ContextId, Empty -from .Objects import ( - CONTEXT_ID, CONTEXTS, DEVICE_O1_UUID, DEVICE_R1_UUID, DEVICE_R3_UUID, DEVICES, LINKS, TOPOLOGIES, WIM_MAPPING, - WIM_PASSWORD, WIM_USERNAME) - - -LOGGER = logging.getLogger(__name__) -LOGGER.setLevel(logging.DEBUG) - -DEVTYPE_EMU_PR = DeviceTypeEnum.EMULATED_PACKET_ROUTER.value -DEVTYPE_EMU_OLS = DeviceTypeEnum.EMULATED_OPTICAL_LINE_SYSTEM.value - - -@pytest.fixture(scope='session') -def context_client(): - _client = ContextClient(get_setting('CONTEXTSERVICE_SERVICE_HOST'), get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')) - yield _client - _client.close() - - -@pytest.fixture(scope='session') -def osm_wim(): - wim_url = 'http://{:s}:{:s}'.format( - get_setting('COMPUTESERVICE_SERVICE_HOST'), str(get_setting('COMPUTESERVICE_SERVICE_PORT_HTTP'))) - return MockOSM(wim_url, WIM_MAPPING, WIM_USERNAME, WIM_PASSWORD) - - -def test_scenario_is_correct(context_client : ContextClient): # pylint: disable=redefined-outer-name - # ----- List entities - Ensure service is created ------------------------------------------------------------------ - response = context_client.ListContexts(Empty()) - assert len(response.contexts) == len(CONTEXTS) - - response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) - assert len(response.topologies) == len(TOPOLOGIES) - - response = context_client.ListDevices(Empty()) - assert len(response.devices) == len(DEVICES) - - response = context_client.ListLinks(Empty()) - assert len(response.links) == len(LINKS) - - response = context_client.ListServices(ContextId(**CONTEXT_ID)) - LOGGER.info('Services[{:d}] = {:s}'.format(len(response.services), grpc_message_to_json_string(response))) - assert len(response.services) == 2 # L3NM + TAPI - for service in response.services: - service_id = service.service_id - response = context_client.ListConnections(service_id) - LOGGER.info(' ServiceId[{:s}] => Connections[{:d}] = {:s}'.format( - grpc_message_to_json_string(service_id), len(response.connections), grpc_message_to_json_string(response))) - assert len(response.connections) == 1 # one connection per service - - -def test_service_removal(context_client : ContextClient, osm_wim : MockOSM): # pylint: disable=redefined-outer-name - # ----- Start the EventsCollector ---------------------------------------------------------------------------------- - events_collector = EventsCollector(context_client, log_events_received=True) - events_collector.start() - - # ----- Delete Service --------------------------------------------------------------------------------------------- - response = context_client.ListServiceIds(ContextId(**CONTEXT_ID)) - LOGGER.info('Services[{:d}] = {:s}'.format(len(response.service_ids), grpc_message_to_json_string(response))) - assert len(response.service_ids) == 2 # L3NM + TAPI - service_uuids = set() - for service_id in response.service_ids: - service_uuid = service_id.service_uuid.uuid - if service_uuid.endswith(':optical'): continue - service_uuids.add(service_uuid) - osm_wim.conn_info[service_uuid] = {} - - assert len(service_uuids) == 1 # assume a single service has been created - service_uuid = set(service_uuids).pop() - - osm_wim.delete_connectivity_service(service_uuid) - - # ----- Validate collected events ---------------------------------------------------------------------------------- - packet_connection_uuid = '{:s}:{:s}'.format(service_uuid, DEVTYPE_EMU_PR) - optical_connection_uuid = '{:s}:optical:{:s}'.format(service_uuid, DEVTYPE_EMU_OLS) - optical_service_uuid = '{:s}:optical'.format(service_uuid) - - expected_events = [ - ('ConnectionEvent', EVENT_REMOVE, json_connection_id(packet_connection_uuid)), - ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_R1_UUID)), - ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_R3_UUID)), - ('ServiceEvent', EVENT_REMOVE, json_service_id(service_uuid, context_id=CONTEXT_ID)), - ('ConnectionEvent', EVENT_REMOVE, json_connection_id(optical_connection_uuid)), - ('DeviceEvent', EVENT_UPDATE, json_device_id(DEVICE_O1_UUID)), - ('ServiceEvent', EVENT_REMOVE, json_service_id(optical_service_uuid, context_id=CONTEXT_ID)), - ] - check_events(events_collector, expected_events) - - # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- - events_collector.stop() - - -def test_services_removed(context_client : ContextClient): # pylint: disable=redefined-outer-name - # ----- List entities - Ensure service is removed ------------------------------------------------------------------ - response = context_client.ListContexts(Empty()) - assert len(response.contexts) == len(CONTEXTS) - - response = context_client.ListTopologies(ContextId(**CONTEXT_ID)) - assert len(response.topologies) == len(TOPOLOGIES) - - response = context_client.ListDevices(Empty()) - assert len(response.devices) == len(DEVICES) - - response = context_client.ListLinks(Empty()) - assert len(response.links) == len(LINKS) - - response = context_client.ListServices(ContextId(**CONTEXT_ID)) - assert len(response.services) == 0 -- GitLab From 482e901193386f79b49a4f86e23f7d69e9b0ec9f Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 12:13:08 +0200 Subject: [PATCH 027/353] Updated genproto scripts to include header files and updated existing files. --- src/compute/genproto.sh | 17 ++++++++++++++++- src/compute/proto/__init__.py | 14 ++++++++++++++ src/context/genproto.sh | 17 ++++++++++++++++- src/context/proto/__init__.py | 14 ++++++++++++++ src/dbscanserving/genproto.sh | 17 ++++++++++++++++- src/dbscanserving/proto/__init__.py | 14 ++++++++++++++ src/device/genproto.sh | 17 ++++++++++++++++- src/device/proto/__init__.py | 14 ++++++++++++++ src/interdomain/genproto.sh | 17 ++++++++++++++++- src/interdomain/proto/__init__.py | 14 ++++++++++++++ src/l3_attackmitigator/genproto.sh | 17 ++++++++++++++++- src/l3_attackmitigator/proto/__init__.py | 14 ++++++++++++++ src/l3_centralizedattackdetector/genproto.sh | 17 ++++++++++++++++- .../proto/__init__.py | 14 ++++++++++++++ src/l3_distributedattackdetector/genproto.sh | 17 ++++++++++++++++- .../proto/__init__.py | 14 ++++++++++++++ src/monitoring/genproto.sh | 17 ++++++++++++++++- src/monitoring/proto/__init__.py | 14 ++++++++++++++ src/opticalattackmitigator/genproto.sh | 17 ++++++++++++++++- src/opticalattackmitigator/proto/__init__.py | 14 ++++++++++++++ .../genproto.sh | 17 ++++++++++++++++- .../proto/__init__.py | 14 ++++++++++++++ src/service/genproto.sh | 17 ++++++++++++++++- src/service/proto/__init__.py | 14 ++++++++++++++ src/slice/genproto.sh | 17 ++++++++++++++++- src/slice/proto/__init__.py | 14 ++++++++++++++ src/webui/genproto.sh | 17 ++++++++++++++++- src/webui/proto/__init__.py | 14 ++++++++++++++ 28 files changed, 420 insertions(+), 14 deletions(-) diff --git a/src/compute/genproto.sh b/src/compute/genproto.sh index 27ad91b29..0c0245c3e 100755 --- a/src/compute/genproto.sh +++ b/src/compute/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF 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 diff --git a/src/compute/proto/__init__.py b/src/compute/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/compute/proto/__init__.py +++ b/src/compute/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/context/genproto.sh b/src/context/genproto.sh index 7d3a0afc5..5c54cd7a2 100755 --- a/src/context/genproto.sh +++ b/src/context/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF 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 kpi_sample_types.proto diff --git a/src/context/proto/__init__.py b/src/context/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/context/proto/__init__.py +++ b/src/context/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/dbscanserving/genproto.sh b/src/dbscanserving/genproto.sh index 452324ad7..6c480c673 100755 --- a/src/dbscanserving/genproto.sh +++ b/src/dbscanserving/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF # building current service protos python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto dbscanserving.proto diff --git a/src/dbscanserving/proto/__init__.py b/src/dbscanserving/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/dbscanserving/proto/__init__.py +++ b/src/dbscanserving/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/device/genproto.sh b/src/device/genproto.sh index 795fe4bba..49b1d10ad 100755 --- a/src/device/genproto.sh +++ b/src/device/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF 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 device.proto diff --git a/src/device/proto/__init__.py b/src/device/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/device/proto/__init__.py +++ b/src/device/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/interdomain/genproto.sh b/src/interdomain/genproto.sh index 138b8d640..908b7aed6 100755 --- a/src/interdomain/genproto.sh +++ b/src/interdomain/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF 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 kpi_sample_types.proto diff --git a/src/interdomain/proto/__init__.py b/src/interdomain/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/interdomain/proto/__init__.py +++ b/src/interdomain/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/l3_attackmitigator/genproto.sh b/src/l3_attackmitigator/genproto.sh index 916b6b548..a93501427 100755 --- a/src/l3_attackmitigator/genproto.sh +++ b/src/l3_attackmitigator/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto l3_attackmitigator.proto diff --git a/src/l3_attackmitigator/proto/__init__.py b/src/l3_attackmitigator/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/l3_attackmitigator/proto/__init__.py +++ b/src/l3_attackmitigator/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/l3_centralizedattackdetector/genproto.sh b/src/l3_centralizedattackdetector/genproto.sh index d9f624fae..4c78ef756 100755 --- a/src/l3_centralizedattackdetector/genproto.sh +++ b/src/l3_centralizedattackdetector/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto l3_centralizedattackdetector.proto python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto l3_attackmitigator.proto diff --git a/src/l3_centralizedattackdetector/proto/__init__.py b/src/l3_centralizedattackdetector/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/l3_centralizedattackdetector/proto/__init__.py +++ b/src/l3_centralizedattackdetector/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/l3_distributedattackdetector/genproto.sh b/src/l3_distributedattackdetector/genproto.sh index d67316784..c1f54c0dc 100755 --- a/src/l3_distributedattackdetector/genproto.sh +++ b/src/l3_distributedattackdetector/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto l3_centralizedattackdetector.proto diff --git a/src/l3_distributedattackdetector/proto/__init__.py b/src/l3_distributedattackdetector/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/l3_distributedattackdetector/proto/__init__.py +++ b/src/l3_distributedattackdetector/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/monitoring/genproto.sh b/src/monitoring/genproto.sh index 4f92cd730..793e40ad2 100755 --- a/src/monitoring/genproto.sh +++ b/src/monitoring/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto monitoring.proto python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto context.proto diff --git a/src/monitoring/proto/__init__.py b/src/monitoring/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/monitoring/proto/__init__.py +++ b/src/monitoring/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/opticalattackmitigator/genproto.sh b/src/opticalattackmitigator/genproto.sh index feace22dc..500fd1930 100755 --- a/src/opticalattackmitigator/genproto.sh +++ b/src/opticalattackmitigator/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF # building protos of services used python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto context.proto diff --git a/src/opticalattackmitigator/proto/__init__.py b/src/opticalattackmitigator/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/opticalattackmitigator/proto/__init__.py +++ b/src/opticalattackmitigator/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/opticalcentralizedattackdetector/genproto.sh b/src/opticalcentralizedattackdetector/genproto.sh index 10351cd3d..855231cce 100755 --- a/src/opticalcentralizedattackdetector/genproto.sh +++ b/src/opticalcentralizedattackdetector/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF # building protos of services used python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto context.proto diff --git a/src/opticalcentralizedattackdetector/proto/__init__.py b/src/opticalcentralizedattackdetector/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/opticalcentralizedattackdetector/proto/__init__.py +++ b/src/opticalcentralizedattackdetector/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/service/genproto.sh b/src/service/genproto.sh index 3e147eb94..529054968 100755 --- a/src/service/genproto.sh +++ b/src/service/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF 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 diff --git a/src/service/proto/__init__.py b/src/service/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/service/proto/__init__.py +++ b/src/service/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/slice/genproto.sh b/src/slice/genproto.sh index daa5dbb98..e51905dd2 100755 --- a/src/slice/genproto.sh +++ b/src/slice/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF 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 kpi_sample_types.proto diff --git a/src/slice/proto/__init__.py b/src/slice/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/slice/proto/__init__.py +++ b/src/slice/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/webui/genproto.sh b/src/webui/genproto.sh index 28b0ef9ea..290ae0294 100755 --- a/src/webui/genproto.sh +++ b/src/webui/genproto.sh @@ -19,7 +19,22 @@ cd $(dirname $0) rm -rf proto/*.py rm -rf proto/__pycache__ -touch proto/__init__.py +tee proto/__init__.py << EOF > /dev/null +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +EOF # building protos of services used # python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto compute.proto diff --git a/src/webui/proto/__init__.py b/src/webui/proto/__init__.py index e69de29bb..70a332512 100644 --- a/src/webui/proto/__init__.py +++ b/src/webui/proto/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + -- GitLab From 2f40ff41f185f774ccc73c59f121c23c65b1dd11 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 12:21:35 +0200 Subject: [PATCH 028/353] Added missing headers in Interdomain and Slice component files --- src/interdomain/__init__.py | 14 ++++++++++++++ src/interdomain/client/InterdomainClient.py | 14 ++++++++++++++ src/interdomain/service/RemoteDomainClients.py | 14 ++++++++++++++ src/interdomain/service/__init__.py | 14 ++++++++++++++ src/interdomain/tests/test_unitary.py | 15 +++++++++++++++ src/slice/client/SliceClient.py | 14 ++++++++++++++ src/slice/client/__init__.py | 14 ++++++++++++++ .../old_code/{service => }/ConstraintsChecker.py | 14 ++++++++++++++ src/slice/old_code/{service => }/SliceCheckers.py | 14 ++++++++++++++ src/slice/old_code/Tools.py | 14 ++++++++++++++ src/slice/old_code/Tools_2.py | 14 ++++++++++++++ src/slice/service/__init__.py | 14 ++++++++++++++ src/slice/tests/__init__.py | 14 ++++++++++++++ src/slice/tests/test_unitary.py | 14 ++++++++++++++ 14 files changed, 197 insertions(+) rename src/slice/old_code/{service => }/ConstraintsChecker.py (75%) rename src/slice/old_code/{service => }/SliceCheckers.py (60%) diff --git a/src/interdomain/__init__.py b/src/interdomain/__init__.py index e69de29bb..70a332512 100644 --- a/src/interdomain/__init__.py +++ b/src/interdomain/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/interdomain/client/InterdomainClient.py b/src/interdomain/client/InterdomainClient.py index 985af9c53..345dfa3ec 100644 --- a/src/interdomain/client/InterdomainClient.py +++ b/src/interdomain/client/InterdomainClient.py @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 grpc, logging from common.tools.client.RetryDecorator import retry, delay_exponential from common.tools.grpc.Tools import grpc_message_to_json_string diff --git a/src/interdomain/service/RemoteDomainClients.py b/src/interdomain/service/RemoteDomainClients.py index 98c6a37d0..709aa3c07 100644 --- a/src/interdomain/service/RemoteDomainClients.py +++ b/src/interdomain/service/RemoteDomainClients.py @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, socket from common.Constants import DEFAULT_CONTEXT_UUID from common.Settings import get_setting diff --git a/src/interdomain/service/__init__.py b/src/interdomain/service/__init__.py index e69de29bb..70a332512 100644 --- a/src/interdomain/service/__init__.py +++ b/src/interdomain/service/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/interdomain/tests/test_unitary.py b/src/interdomain/tests/test_unitary.py index 57fe4d891..bcc6bb9c9 100644 --- a/src/interdomain/tests/test_unitary.py +++ b/src/interdomain/tests/test_unitary.py @@ -1,3 +1,18 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, grpc import os import sqlite3 diff --git a/src/slice/client/SliceClient.py b/src/slice/client/SliceClient.py index c30c28002..5566108f8 100644 --- a/src/slice/client/SliceClient.py +++ b/src/slice/client/SliceClient.py @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 grpc, logging from common.tools.client.RetryDecorator import retry, delay_exponential from common.tools.grpc.Tools import grpc_message_to_json_string diff --git a/src/slice/client/__init__.py b/src/slice/client/__init__.py index e69de29bb..70a332512 100644 --- a/src/slice/client/__init__.py +++ b/src/slice/client/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/slice/old_code/service/ConstraintsChecker.py b/src/slice/old_code/ConstraintsChecker.py similarity index 75% rename from src/slice/old_code/service/ConstraintsChecker.py rename to src/slice/old_code/ConstraintsChecker.py index 864cf3ed1..44cf2c76d 100644 --- a/src/slice/old_code/service/ConstraintsChecker.py +++ b/src/slice/old_code/ConstraintsChecker.py @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 grpc, logging from typing import Dict, List, Set, Tuple from common.Checkers import chk_string diff --git a/src/slice/old_code/service/SliceCheckers.py b/src/slice/old_code/SliceCheckers.py similarity index 60% rename from src/slice/old_code/service/SliceCheckers.py rename to src/slice/old_code/SliceCheckers.py index bac9766b8..660a522d0 100644 --- a/src/slice/old_code/service/SliceCheckers.py +++ b/src/slice/old_code/SliceCheckers.py @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 grpc from common.database.api.Database import Database from common.database.api.context.slice.Slice import Slice diff --git a/src/slice/old_code/Tools.py b/src/slice/old_code/Tools.py index d31fed6cb..4029c5b19 100644 --- a/src/slice/old_code/Tools.py +++ b/src/slice/old_code/Tools.py @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 grpc, logging from typing import Dict, List, Set, Tuple from common.Checkers import chk_options, chk_string diff --git a/src/slice/old_code/Tools_2.py b/src/slice/old_code/Tools_2.py index 12bd9bbbc..c29a11a06 100644 --- a/src/slice/old_code/Tools_2.py +++ b/src/slice/old_code/Tools_2.py @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + src/common/tools/service/DeviceCheckers.py import grpc from common.database.api.Database import Database diff --git a/src/slice/service/__init__.py b/src/slice/service/__init__.py index e69de29bb..70a332512 100644 --- a/src/slice/service/__init__.py +++ b/src/slice/service/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/slice/tests/__init__.py b/src/slice/tests/__init__.py index e69de29bb..70a332512 100644 --- a/src/slice/tests/__init__.py +++ b/src/slice/tests/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/slice/tests/test_unitary.py b/src/slice/tests/test_unitary.py index d11ac7100..fb58b5153 100644 --- a/src/slice/tests/test_unitary.py +++ b/src/slice/tests/test_unitary.py @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 copy, grpc, logging, pytest from common.database.Factory import get_database, DatabaseEngineEnum from slice.client.SliceClient import SliceClient -- GitLab From 20023ac67254dd4f361ce87662500875cc74adb0 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 12:26:36 +0200 Subject: [PATCH 029/353] Added missing headers --- .gitlab-ci.yml | 14 ++++++++++++++ manifests/sliceservice.yaml | 14 ++++++++++++++ src/interdomain/.gitlab-ci.yml | 14 ++++++++++++++ src/slice/.gitlab-ci.yml | 14 ++++++++++++++ src/tests/oeccpsc22/dump_logs.sh | 13 +++++++++++++ 5 files changed, 69 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f163058c5..35f5f12ee 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + # stages of the cicd pipeline stages: - dependencies diff --git a/manifests/sliceservice.yaml b/manifests/sliceservice.yaml index 5c4b8855f..eeed3776c 100644 --- a/manifests/sliceservice.yaml +++ b/manifests/sliceservice.yaml @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + apiVersion: apps/v1 kind: Deployment metadata: diff --git a/src/interdomain/.gitlab-ci.yml b/src/interdomain/.gitlab-ci.yml index e4da10db7..f4dd49fd0 100644 --- a/src/interdomain/.gitlab-ci.yml +++ b/src/interdomain/.gitlab-ci.yml @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + # Build, tag and push the Docker image to the GitLab registry build service: variables: diff --git a/src/slice/.gitlab-ci.yml b/src/slice/.gitlab-ci.yml index 3ba7429f9..d62e8edad 100644 --- a/src/slice/.gitlab-ci.yml +++ b/src/slice/.gitlab-ci.yml @@ -1,3 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + # Build, tag, and push the Docker images to the GitLab Docker registry build slice: variables: diff --git a/src/tests/oeccpsc22/dump_logs.sh b/src/tests/oeccpsc22/dump_logs.sh index c3c2e6284..196002a5f 100755 --- a/src/tests/oeccpsc22/dump_logs.sh +++ b/src/tests/oeccpsc22/dump_logs.sh @@ -1,4 +1,17 @@ #!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. mkdir -p tmp/exec_logs/ -- GitLab From 76c09cabd59bae9d7bbc5931e14b8055b3fbd190 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 12:37:37 +0200 Subject: [PATCH 030/353] Added missing headers --- src/slice/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/slice/__init__.py b/src/slice/__init__.py index e69de29bb..70a332512 100644 --- a/src/slice/__init__.py +++ b/src/slice/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + -- GitLab From 52b9484336da242c067909d37306876e5a7235fd Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 12:58:26 +0200 Subject: [PATCH 031/353] Corrected genproto script for l3_attackmitigator. --- src/l3_attackmitigator/genproto.sh | 4 + src/l3_attackmitigator/proto/context_pb2.py | 3071 +++++++++++++++++++ 2 files changed, 3075 insertions(+) create mode 100644 src/l3_attackmitigator/proto/context_pb2.py diff --git a/src/l3_attackmitigator/genproto.sh b/src/l3_attackmitigator/genproto.sh index a93501427..8b6092c8d 100755 --- a/src/l3_attackmitigator/genproto.sh +++ b/src/l3_attackmitigator/genproto.sh @@ -36,7 +36,11 @@ tee proto/__init__.py << EOF > /dev/null EOF +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 l3_attackmitigator.proto +rm proto/context_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/l3_attackmitigator_pb2.py sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/l3_attackmitigator_pb2_grpc.py diff --git a/src/l3_attackmitigator/proto/context_pb2.py b/src/l3_attackmitigator/proto/context_pb2.py new file mode 100644 index 000000000..50d501d3a --- /dev/null +++ b/src/l3_attackmitigator/proto/context_pb2.py @@ -0,0 +1,3071 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: context.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 kpi_sample_types_pb2 as kpi__sample__types__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='context.proto', + package='context', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x95\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12-\n\x11slice_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + , + dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) + +_EVENTTYPEENUM = _descriptor.EnumDescriptor( + name='EventTypeEnum', + full_name='context.EventTypeEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_CREATE', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_UPDATE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_REMOVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4310, + serialized_end=4416, +) +_sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) + +EventTypeEnum = enum_type_wrapper.EnumTypeWrapper(_EVENTTYPEENUM) +_DEVICEDRIVERENUM = _descriptor.EnumDescriptor( + name='DeviceDriverEnum', + full_name='context.DeviceDriverEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_OPENCONFIG', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_TRANSPORT_API', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_P4', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_IETF_NETWORK_TOPOLOGY', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_ONF_TR_352', index=5, number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4419, + serialized_end=4616, +) +_sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) + +DeviceDriverEnum = enum_type_wrapper.EnumTypeWrapper(_DEVICEDRIVERENUM) +_DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( + name='DeviceOperationalStatusEnum', + full_name='context.DeviceOperationalStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DEVICEOPERATIONALSTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEOPERATIONALSTATUS_DISABLED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEOPERATIONALSTATUS_ENABLED', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4619, + serialized_end=4762, +) +_sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) + +DeviceOperationalStatusEnum = enum_type_wrapper.EnumTypeWrapper(_DEVICEOPERATIONALSTATUSENUM) +_SERVICETYPEENUM = _descriptor.EnumDescriptor( + name='ServiceTypeEnum', + full_name='context.ServiceTypeEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_L3NM', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_L2NM', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_TAPI_CONNECTIVITY_SERVICE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4765, + serialized_end=4894, +) +_sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) + +ServiceTypeEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICETYPEENUM) +_SERVICESTATUSENUM = _descriptor.EnumDescriptor( + name='ServiceStatusEnum', + full_name='context.ServiceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_ACTIVE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_PENDING_REMOVAL', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4897, + serialized_end=5033, +) +_sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) + +ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_SLICESTATUSENUM = _descriptor.EnumDescriptor( + name='SliceStatusEnum', + full_name='context.SliceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_INIT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_ACTIVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_DEINIT', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5036, + serialized_end=5175, +) +_sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) + +SliceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SLICESTATUSENUM) +_CONFIGACTIONENUM = _descriptor.EnumDescriptor( + name='ConfigActionEnum', + full_name='context.ConfigActionEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='CONFIGACTION_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CONFIGACTION_SET', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CONFIGACTION_DELETE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5177, + serialized_end=5270, +) +_sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) + +ConfigActionEnum = enum_type_wrapper.EnumTypeWrapper(_CONFIGACTIONENUM) +EVENTTYPE_UNDEFINED = 0 +EVENTTYPE_CREATE = 1 +EVENTTYPE_UPDATE = 2 +EVENTTYPE_REMOVE = 3 +DEVICEDRIVER_UNDEFINED = 0 +DEVICEDRIVER_OPENCONFIG = 1 +DEVICEDRIVER_TRANSPORT_API = 2 +DEVICEDRIVER_P4 = 3 +DEVICEDRIVER_IETF_NETWORK_TOPOLOGY = 4 +DEVICEDRIVER_ONF_TR_352 = 5 +DEVICEOPERATIONALSTATUS_UNDEFINED = 0 +DEVICEOPERATIONALSTATUS_DISABLED = 1 +DEVICEOPERATIONALSTATUS_ENABLED = 2 +SERVICETYPE_UNKNOWN = 0 +SERVICETYPE_L3NM = 1 +SERVICETYPE_L2NM = 2 +SERVICETYPE_TAPI_CONNECTIVITY_SERVICE = 3 +SERVICESTATUS_UNDEFINED = 0 +SERVICESTATUS_PLANNED = 1 +SERVICESTATUS_ACTIVE = 2 +SERVICESTATUS_PENDING_REMOVAL = 3 +SLICESTATUS_UNDEFINED = 0 +SLICESTATUS_PLANNED = 1 +SLICESTATUS_INIT = 2 +SLICESTATUS_ACTIVE = 3 +SLICESTATUS_DEINIT = 4 +CONFIGACTION_UNDEFINED = 0 +CONFIGACTION_SET = 1 +CONFIGACTION_DELETE = 2 + + + +_EMPTY = _descriptor.Descriptor( + name='Empty', + full_name='context.Empty', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=50, + serialized_end=57, +) + + +_UUID = _descriptor.Descriptor( + name='Uuid', + full_name='context.Uuid', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='uuid', full_name='context.Uuid.uuid', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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=59, + serialized_end=79, +) + + +_EVENT = _descriptor.Descriptor( + name='Event', + full_name='context.Event', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='timestamp', full_name='context.Event.timestamp', index=0, + number=1, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(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), + _descriptor.FieldDescriptor( + name='event_type', full_name='context.Event.event_type', 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=81, + serialized_end=151, +) + + +_CONTEXTID = _descriptor.Descriptor( + name='ContextId', + full_name='context.ContextId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_uuid', full_name='context.ContextId.context_uuid', 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=153, + serialized_end=201, +) + + +_CONTEXT = _descriptor.Descriptor( + name='Context', + full_name='context.Context', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.Context.context_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='topology_ids', full_name='context.Context.topology_ids', 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='service_ids', full_name='context.Context.service_ids', 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='controller', full_name='context.Context.controller', index=3, + number=4, 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=204, + serialized_end=386, +) + + +_CONTEXTIDLIST = _descriptor.Descriptor( + name='ContextIdList', + full_name='context.ContextIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_ids', full_name='context.ContextIdList.context_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=388, + serialized_end=444, +) + + +_CONTEXTLIST = _descriptor.Descriptor( + name='ContextList', + full_name='context.ContextList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='contexts', full_name='context.ContextList.contexts', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=446, + serialized_end=495, +) + + +_CONTEXTEVENT = _descriptor.Descriptor( + name='ContextEvent', + full_name='context.ContextEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.ContextEvent.event', 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='context_id', full_name='context.ContextEvent.context_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=497, + serialized_end=582, +) + + +_TOPOLOGYID = _descriptor.Descriptor( + name='TopologyId', + full_name='context.TopologyId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.TopologyId.context_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='topology_uuid', full_name='context.TopologyId.topology_uuid', 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=584, + serialized_end=674, +) + + +_TOPOLOGY = _descriptor.Descriptor( + name='Topology', + full_name='context.Topology', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topology_id', full_name='context.Topology.topology_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='device_ids', full_name='context.Topology.device_ids', 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='link_ids', full_name='context.Topology.link_ids', 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=676, + serialized_end=802, +) + + +_TOPOLOGYIDLIST = _descriptor.Descriptor( + name='TopologyIdList', + full_name='context.TopologyIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topology_ids', full_name='context.TopologyIdList.topology_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=804, + serialized_end=863, +) + + +_TOPOLOGYLIST = _descriptor.Descriptor( + name='TopologyList', + full_name='context.TopologyList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topologies', full_name='context.TopologyList.topologies', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=865, + serialized_end=918, +) + + +_TOPOLOGYEVENT = _descriptor.Descriptor( + name='TopologyEvent', + full_name='context.TopologyEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.TopologyEvent.event', 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='topology_id', full_name='context.TopologyEvent.topology_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=920, + serialized_end=1008, +) + + +_DEVICEID = _descriptor.Descriptor( + name='DeviceId', + full_name='context.DeviceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='device_uuid', full_name='context.DeviceId.device_uuid', 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=1010, + serialized_end=1056, +) + + +_DEVICE = _descriptor.Descriptor( + name='Device', + full_name='context.Device', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='device_id', full_name='context.Device.device_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='device_type', full_name='context.Device.device_type', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='device_config', full_name='context.Device.device_config', index=2, + number=3, 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='device_operational_status', full_name='context.Device.device_operational_status', index=3, + number=4, 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), + _descriptor.FieldDescriptor( + name='device_drivers', full_name='context.Device.device_drivers', index=4, + number=5, type=14, cpp_type=8, 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='device_endpoints', full_name='context.Device.device_endpoints', index=5, + number=6, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1059, + serialized_end=1341, +) + + +_DEVICECONFIG = _descriptor.Descriptor( + name='DeviceConfig', + full_name='context.DeviceConfig', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='config_rules', full_name='context.DeviceConfig.config_rules', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1343, + serialized_end=1400, +) + + +_DEVICEIDLIST = _descriptor.Descriptor( + name='DeviceIdList', + full_name='context.DeviceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='device_ids', full_name='context.DeviceIdList.device_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1402, + serialized_end=1455, +) + + +_DEVICELIST = _descriptor.Descriptor( + name='DeviceList', + full_name='context.DeviceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='devices', full_name='context.DeviceList.devices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1457, + serialized_end=1503, +) + + +_DEVICEEVENT = _descriptor.Descriptor( + name='DeviceEvent', + full_name='context.DeviceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.DeviceEvent.event', 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='device_id', full_name='context.DeviceEvent.device_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=1505, + serialized_end=1587, +) + + +_LINKID = _descriptor.Descriptor( + name='LinkId', + full_name='context.LinkId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='link_uuid', full_name='context.LinkId.link_uuid', 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=1589, + serialized_end=1631, +) + + +_LINK = _descriptor.Descriptor( + name='Link', + full_name='context.Link', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='link_id', full_name='context.Link.link_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='link_endpoint_ids', full_name='context.Link.link_endpoint_ids', 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1633, + serialized_end=1721, +) + + +_LINKIDLIST = _descriptor.Descriptor( + name='LinkIdList', + full_name='context.LinkIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='link_ids', full_name='context.LinkIdList.link_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1723, + serialized_end=1770, +) + + +_LINKLIST = _descriptor.Descriptor( + name='LinkList', + full_name='context.LinkList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='links', full_name='context.LinkList.links', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1772, + serialized_end=1812, +) + + +_LINKEVENT = _descriptor.Descriptor( + name='LinkEvent', + full_name='context.LinkEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.LinkEvent.event', 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='link_id', full_name='context.LinkEvent.link_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=1814, + serialized_end=1890, +) + + +_SERVICEID = _descriptor.Descriptor( + name='ServiceId', + full_name='context.ServiceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.ServiceId.context_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='service_uuid', full_name='context.ServiceId.service_uuid', 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=1892, + serialized_end=1980, +) + + +_SERVICE = _descriptor.Descriptor( + name='Service', + full_name='context.Service', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='service_id', full_name='context.Service.service_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='service_type', full_name='context.Service.service_type', 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), + _descriptor.FieldDescriptor( + name='service_endpoint_ids', full_name='context.Service.service_endpoint_ids', 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='service_constraints', full_name='context.Service.service_constraints', 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='service_status', full_name='context.Service.service_status', index=4, + number=5, 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='service_config', full_name='context.Service.service_config', 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=1983, + serialized_end=2277, +) + + +_SERVICESTATUS = _descriptor.Descriptor( + name='ServiceStatus', + full_name='context.ServiceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='service_status', full_name='context.ServiceStatus.service_status', index=0, + number=1, 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=2279, + serialized_end=2346, +) + + +_SERVICECONFIG = _descriptor.Descriptor( + name='ServiceConfig', + full_name='context.ServiceConfig', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='config_rules', full_name='context.ServiceConfig.config_rules', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2348, + serialized_end=2406, +) + + +_SERVICEIDLIST = _descriptor.Descriptor( + name='ServiceIdList', + full_name='context.ServiceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='service_ids', full_name='context.ServiceIdList.service_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2408, + serialized_end=2464, +) + + +_SERVICELIST = _descriptor.Descriptor( + name='ServiceList', + full_name='context.ServiceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='services', full_name='context.ServiceList.services', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2466, + serialized_end=2515, +) + + +_SERVICEEVENT = _descriptor.Descriptor( + name='ServiceEvent', + full_name='context.ServiceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.ServiceEvent.event', 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='service_id', full_name='context.ServiceEvent.service_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=2517, + serialized_end=2602, +) + + +_SLICEID = _descriptor.Descriptor( + name='SliceId', + full_name='context.SliceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.SliceId.context_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='slice_uuid', full_name='context.SliceId.slice_uuid', 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=2604, + serialized_end=2688, +) + + +_SLICE = _descriptor.Descriptor( + name='Slice', + full_name='context.Slice', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_id', full_name='context.Slice.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='slice_endpoint_ids', full_name='context.Slice.slice_endpoint_ids', 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='slice_constraints', full_name='context.Slice.slice_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='slice_service_ids', full_name='context.Slice.slice_service_ids', 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='slice_subslice_ids', full_name='context.Slice.slice_subslice_ids', 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='slice_status', full_name='context.Slice.slice_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=2691, + serialized_end=2968, +) + + +_SLICESTATUS = _descriptor.Descriptor( + name='SliceStatus', + full_name='context.SliceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_status', full_name='context.SliceStatus.slice_status', index=0, + number=1, 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=2970, + serialized_end=3031, +) + + +_SLICEIDLIST = _descriptor.Descriptor( + name='SliceIdList', + full_name='context.SliceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_ids', full_name='context.SliceIdList.slice_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3033, + serialized_end=3083, +) + + +_SLICELIST = _descriptor.Descriptor( + name='SliceList', + full_name='context.SliceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slices', full_name='context.SliceList.slices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3085, + serialized_end=3128, +) + + +_SLICEEVENT = _descriptor.Descriptor( + name='SliceEvent', + full_name='context.SliceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.SliceEvent.event', 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='context.SliceEvent.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=3130, + serialized_end=3209, +) + + +_CONNECTIONID = _descriptor.Descriptor( + name='ConnectionId', + full_name='context.ConnectionId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connection_uuid', full_name='context.ConnectionId.connection_uuid', 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=3211, + serialized_end=3265, +) + + +_CONNECTION = _descriptor.Descriptor( + name='Connection', + full_name='context.Connection', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connection_id', full_name='context.Connection.connection_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='service_id', full_name='context.Connection.service_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), + _descriptor.FieldDescriptor( + name='path_hops_endpoint_ids', full_name='context.Connection.path_hops_endpoint_ids', 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='sub_service_ids', full_name='context.Connection.sub_service_ids', 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3268, + serialized_end=3464, +) + + +_CONNECTIONIDLIST = _descriptor.Descriptor( + name='ConnectionIdList', + full_name='context.ConnectionIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connection_ids', full_name='context.ConnectionIdList.connection_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3466, + serialized_end=3531, +) + + +_CONNECTIONLIST = _descriptor.Descriptor( + name='ConnectionList', + full_name='context.ConnectionList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connections', full_name='context.ConnectionList.connections', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3533, + serialized_end=3591, +) + + +_CONNECTIONEVENT = _descriptor.Descriptor( + name='ConnectionEvent', + full_name='context.ConnectionEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.ConnectionEvent.event', 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='connection_id', full_name='context.ConnectionEvent.connection_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=3593, + serialized_end=3687, +) + + +_ENDPOINTID = _descriptor.Descriptor( + name='EndPointId', + full_name='context.EndPointId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topology_id', full_name='context.EndPointId.topology_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='device_id', full_name='context.EndPointId.device_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), + _descriptor.FieldDescriptor( + name='endpoint_uuid', full_name='context.EndPointId.endpoint_uuid', index=2, + number=3, 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=3690, + serialized_end=3820, +) + + +_ENDPOINT = _descriptor.Descriptor( + name='EndPoint', + full_name='context.EndPoint', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='endpoint_id', full_name='context.EndPoint.endpoint_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='endpoint_type', full_name='context.EndPoint.endpoint_type', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='kpi_sample_types', full_name='context.EndPoint.kpi_sample_types', index=2, + number=3, type=14, cpp_type=8, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3823, + serialized_end=3957, +) + + +_CONFIGRULE = _descriptor.Descriptor( + name='ConfigRule', + full_name='context.ConfigRule', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='action', full_name='context.ConfigRule.action', index=0, + number=1, 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), + _descriptor.FieldDescriptor( + name='resource_key', full_name='context.ConfigRule.resource_key', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='resource_value', full_name='context.ConfigRule.resource_value', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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=3959, + serialized_end=4060, +) + + +_CONSTRAINT = _descriptor.Descriptor( + name='Constraint', + full_name='context.Constraint', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='constraint_type', full_name='context.Constraint.constraint_type', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='constraint_value', full_name='context.Constraint.constraint_value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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=4062, + serialized_end=4125, +) + + +_TERAFLOWCONTROLLER = _descriptor.Descriptor( + name='TeraFlowController', + full_name='context.TeraFlowController', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.TeraFlowController.context_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='ip_address', full_name='context.TeraFlowController.ip_address', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='port', full_name='context.TeraFlowController.port', index=2, + number=3, type=13, cpp_type=3, 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=4127, + serialized_end=4221, +) + + +_AUTHENTICATIONRESULT = _descriptor.Descriptor( + name='AuthenticationResult', + full_name='context.AuthenticationResult', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.AuthenticationResult.context_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='authenticated', full_name='context.AuthenticationResult.authenticated', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + 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=4223, + serialized_end=4308, +) + +_EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM +_CONTEXTID.fields_by_name['context_uuid'].message_type = _UUID +_CONTEXT.fields_by_name['context_id'].message_type = _CONTEXTID +_CONTEXT.fields_by_name['topology_ids'].message_type = _TOPOLOGYID +_CONTEXT.fields_by_name['service_ids'].message_type = _SERVICEID +_CONTEXT.fields_by_name['controller'].message_type = _TERAFLOWCONTROLLER +_CONTEXTIDLIST.fields_by_name['context_ids'].message_type = _CONTEXTID +_CONTEXTLIST.fields_by_name['contexts'].message_type = _CONTEXT +_CONTEXTEVENT.fields_by_name['event'].message_type = _EVENT +_CONTEXTEVENT.fields_by_name['context_id'].message_type = _CONTEXTID +_TOPOLOGYID.fields_by_name['context_id'].message_type = _CONTEXTID +_TOPOLOGYID.fields_by_name['topology_uuid'].message_type = _UUID +_TOPOLOGY.fields_by_name['topology_id'].message_type = _TOPOLOGYID +_TOPOLOGY.fields_by_name['device_ids'].message_type = _DEVICEID +_TOPOLOGY.fields_by_name['link_ids'].message_type = _LINKID +_TOPOLOGYIDLIST.fields_by_name['topology_ids'].message_type = _TOPOLOGYID +_TOPOLOGYLIST.fields_by_name['topologies'].message_type = _TOPOLOGY +_TOPOLOGYEVENT.fields_by_name['event'].message_type = _EVENT +_TOPOLOGYEVENT.fields_by_name['topology_id'].message_type = _TOPOLOGYID +_DEVICEID.fields_by_name['device_uuid'].message_type = _UUID +_DEVICE.fields_by_name['device_id'].message_type = _DEVICEID +_DEVICE.fields_by_name['device_config'].message_type = _DEVICECONFIG +_DEVICE.fields_by_name['device_operational_status'].enum_type = _DEVICEOPERATIONALSTATUSENUM +_DEVICE.fields_by_name['device_drivers'].enum_type = _DEVICEDRIVERENUM +_DEVICE.fields_by_name['device_endpoints'].message_type = _ENDPOINT +_DEVICECONFIG.fields_by_name['config_rules'].message_type = _CONFIGRULE +_DEVICEIDLIST.fields_by_name['device_ids'].message_type = _DEVICEID +_DEVICELIST.fields_by_name['devices'].message_type = _DEVICE +_DEVICEEVENT.fields_by_name['event'].message_type = _EVENT +_DEVICEEVENT.fields_by_name['device_id'].message_type = _DEVICEID +_LINKID.fields_by_name['link_uuid'].message_type = _UUID +_LINK.fields_by_name['link_id'].message_type = _LINKID +_LINK.fields_by_name['link_endpoint_ids'].message_type = _ENDPOINTID +_LINKIDLIST.fields_by_name['link_ids'].message_type = _LINKID +_LINKLIST.fields_by_name['links'].message_type = _LINK +_LINKEVENT.fields_by_name['event'].message_type = _EVENT +_LINKEVENT.fields_by_name['link_id'].message_type = _LINKID +_SERVICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SERVICEID.fields_by_name['service_uuid'].message_type = _UUID +_SERVICE.fields_by_name['service_id'].message_type = _SERVICEID +_SERVICE.fields_by_name['service_type'].enum_type = _SERVICETYPEENUM +_SERVICE.fields_by_name['service_endpoint_ids'].message_type = _ENDPOINTID +_SERVICE.fields_by_name['service_constraints'].message_type = _CONSTRAINT +_SERVICE.fields_by_name['service_status'].message_type = _SERVICESTATUS +_SERVICE.fields_by_name['service_config'].message_type = _SERVICECONFIG +_SERVICESTATUS.fields_by_name['service_status'].enum_type = _SERVICESTATUSENUM +_SERVICECONFIG.fields_by_name['config_rules'].message_type = _CONFIGRULE +_SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID +_SERVICELIST.fields_by_name['services'].message_type = _SERVICE +_SERVICEEVENT.fields_by_name['event'].message_type = _EVENT +_SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_SLICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SLICEID.fields_by_name['slice_uuid'].message_type = _UUID +_SLICE.fields_by_name['slice_id'].message_type = _SLICEID +_SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID +_SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT +_SLICE.fields_by_name['slice_service_ids'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID +_SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS +_SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM +_SLICEIDLIST.fields_by_name['slice_ids'].message_type = _SLICEID +_SLICELIST.fields_by_name['slices'].message_type = _SLICE +_SLICEEVENT.fields_by_name['event'].message_type = _EVENT +_SLICEEVENT.fields_by_name['slice_id'].message_type = _SLICEID +_CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID +_CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID +_CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID +_CONNECTION.fields_by_name['path_hops_endpoint_ids'].message_type = _ENDPOINTID +_CONNECTION.fields_by_name['sub_service_ids'].message_type = _SERVICEID +_CONNECTIONIDLIST.fields_by_name['connection_ids'].message_type = _CONNECTIONID +_CONNECTIONLIST.fields_by_name['connections'].message_type = _CONNECTION +_CONNECTIONEVENT.fields_by_name['event'].message_type = _EVENT +_CONNECTIONEVENT.fields_by_name['connection_id'].message_type = _CONNECTIONID +_ENDPOINTID.fields_by_name['topology_id'].message_type = _TOPOLOGYID +_ENDPOINTID.fields_by_name['device_id'].message_type = _DEVICEID +_ENDPOINTID.fields_by_name['endpoint_uuid'].message_type = _UUID +_ENDPOINT.fields_by_name['endpoint_id'].message_type = _ENDPOINTID +_ENDPOINT.fields_by_name['kpi_sample_types'].enum_type = kpi__sample__types__pb2._KPISAMPLETYPE +_CONFIGRULE.fields_by_name['action'].enum_type = _CONFIGACTIONENUM +_TERAFLOWCONTROLLER.fields_by_name['context_id'].message_type = _CONTEXTID +_AUTHENTICATIONRESULT.fields_by_name['context_id'].message_type = _CONTEXTID +DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY +DESCRIPTOR.message_types_by_name['Uuid'] = _UUID +DESCRIPTOR.message_types_by_name['Event'] = _EVENT +DESCRIPTOR.message_types_by_name['ContextId'] = _CONTEXTID +DESCRIPTOR.message_types_by_name['Context'] = _CONTEXT +DESCRIPTOR.message_types_by_name['ContextIdList'] = _CONTEXTIDLIST +DESCRIPTOR.message_types_by_name['ContextList'] = _CONTEXTLIST +DESCRIPTOR.message_types_by_name['ContextEvent'] = _CONTEXTEVENT +DESCRIPTOR.message_types_by_name['TopologyId'] = _TOPOLOGYID +DESCRIPTOR.message_types_by_name['Topology'] = _TOPOLOGY +DESCRIPTOR.message_types_by_name['TopologyIdList'] = _TOPOLOGYIDLIST +DESCRIPTOR.message_types_by_name['TopologyList'] = _TOPOLOGYLIST +DESCRIPTOR.message_types_by_name['TopologyEvent'] = _TOPOLOGYEVENT +DESCRIPTOR.message_types_by_name['DeviceId'] = _DEVICEID +DESCRIPTOR.message_types_by_name['Device'] = _DEVICE +DESCRIPTOR.message_types_by_name['DeviceConfig'] = _DEVICECONFIG +DESCRIPTOR.message_types_by_name['DeviceIdList'] = _DEVICEIDLIST +DESCRIPTOR.message_types_by_name['DeviceList'] = _DEVICELIST +DESCRIPTOR.message_types_by_name['DeviceEvent'] = _DEVICEEVENT +DESCRIPTOR.message_types_by_name['LinkId'] = _LINKID +DESCRIPTOR.message_types_by_name['Link'] = _LINK +DESCRIPTOR.message_types_by_name['LinkIdList'] = _LINKIDLIST +DESCRIPTOR.message_types_by_name['LinkList'] = _LINKLIST +DESCRIPTOR.message_types_by_name['LinkEvent'] = _LINKEVENT +DESCRIPTOR.message_types_by_name['ServiceId'] = _SERVICEID +DESCRIPTOR.message_types_by_name['Service'] = _SERVICE +DESCRIPTOR.message_types_by_name['ServiceStatus'] = _SERVICESTATUS +DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG +DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST +DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST +DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['SliceId'] = _SLICEID +DESCRIPTOR.message_types_by_name['Slice'] = _SLICE +DESCRIPTOR.message_types_by_name['SliceStatus'] = _SLICESTATUS +DESCRIPTOR.message_types_by_name['SliceIdList'] = _SLICEIDLIST +DESCRIPTOR.message_types_by_name['SliceList'] = _SLICELIST +DESCRIPTOR.message_types_by_name['SliceEvent'] = _SLICEEVENT +DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID +DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION +DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST +DESCRIPTOR.message_types_by_name['ConnectionList'] = _CONNECTIONLIST +DESCRIPTOR.message_types_by_name['ConnectionEvent'] = _CONNECTIONEVENT +DESCRIPTOR.message_types_by_name['EndPointId'] = _ENDPOINTID +DESCRIPTOR.message_types_by_name['EndPoint'] = _ENDPOINT +DESCRIPTOR.message_types_by_name['ConfigRule'] = _CONFIGRULE +DESCRIPTOR.message_types_by_name['Constraint'] = _CONSTRAINT +DESCRIPTOR.message_types_by_name['TeraFlowController'] = _TERAFLOWCONTROLLER +DESCRIPTOR.message_types_by_name['AuthenticationResult'] = _AUTHENTICATIONRESULT +DESCRIPTOR.enum_types_by_name['EventTypeEnum'] = _EVENTTYPEENUM +DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM +DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM +DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM +DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['SliceStatusEnum'] = _SLICESTATUSENUM +DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), { + 'DESCRIPTOR' : _EMPTY, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Empty) + }) +_sym_db.RegisterMessage(Empty) + +Uuid = _reflection.GeneratedProtocolMessageType('Uuid', (_message.Message,), { + 'DESCRIPTOR' : _UUID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Uuid) + }) +_sym_db.RegisterMessage(Uuid) + +Event = _reflection.GeneratedProtocolMessageType('Event', (_message.Message,), { + 'DESCRIPTOR' : _EVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Event) + }) +_sym_db.RegisterMessage(Event) + +ContextId = _reflection.GeneratedProtocolMessageType('ContextId', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextId) + }) +_sym_db.RegisterMessage(ContextId) + +Context = _reflection.GeneratedProtocolMessageType('Context', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Context) + }) +_sym_db.RegisterMessage(Context) + +ContextIdList = _reflection.GeneratedProtocolMessageType('ContextIdList', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextIdList) + }) +_sym_db.RegisterMessage(ContextIdList) + +ContextList = _reflection.GeneratedProtocolMessageType('ContextList', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextList) + }) +_sym_db.RegisterMessage(ContextList) + +ContextEvent = _reflection.GeneratedProtocolMessageType('ContextEvent', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextEvent) + }) +_sym_db.RegisterMessage(ContextEvent) + +TopologyId = _reflection.GeneratedProtocolMessageType('TopologyId', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyId) + }) +_sym_db.RegisterMessage(TopologyId) + +Topology = _reflection.GeneratedProtocolMessageType('Topology', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGY, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Topology) + }) +_sym_db.RegisterMessage(Topology) + +TopologyIdList = _reflection.GeneratedProtocolMessageType('TopologyIdList', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyIdList) + }) +_sym_db.RegisterMessage(TopologyIdList) + +TopologyList = _reflection.GeneratedProtocolMessageType('TopologyList', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyList) + }) +_sym_db.RegisterMessage(TopologyList) + +TopologyEvent = _reflection.GeneratedProtocolMessageType('TopologyEvent', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyEvent) + }) +_sym_db.RegisterMessage(TopologyEvent) + +DeviceId = _reflection.GeneratedProtocolMessageType('DeviceId', (_message.Message,), { + 'DESCRIPTOR' : _DEVICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceId) + }) +_sym_db.RegisterMessage(DeviceId) + +Device = _reflection.GeneratedProtocolMessageType('Device', (_message.Message,), { + 'DESCRIPTOR' : _DEVICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Device) + }) +_sym_db.RegisterMessage(Device) + +DeviceConfig = _reflection.GeneratedProtocolMessageType('DeviceConfig', (_message.Message,), { + 'DESCRIPTOR' : _DEVICECONFIG, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceConfig) + }) +_sym_db.RegisterMessage(DeviceConfig) + +DeviceIdList = _reflection.GeneratedProtocolMessageType('DeviceIdList', (_message.Message,), { + 'DESCRIPTOR' : _DEVICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceIdList) + }) +_sym_db.RegisterMessage(DeviceIdList) + +DeviceList = _reflection.GeneratedProtocolMessageType('DeviceList', (_message.Message,), { + 'DESCRIPTOR' : _DEVICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceList) + }) +_sym_db.RegisterMessage(DeviceList) + +DeviceEvent = _reflection.GeneratedProtocolMessageType('DeviceEvent', (_message.Message,), { + 'DESCRIPTOR' : _DEVICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceEvent) + }) +_sym_db.RegisterMessage(DeviceEvent) + +LinkId = _reflection.GeneratedProtocolMessageType('LinkId', (_message.Message,), { + 'DESCRIPTOR' : _LINKID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkId) + }) +_sym_db.RegisterMessage(LinkId) + +Link = _reflection.GeneratedProtocolMessageType('Link', (_message.Message,), { + 'DESCRIPTOR' : _LINK, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Link) + }) +_sym_db.RegisterMessage(Link) + +LinkIdList = _reflection.GeneratedProtocolMessageType('LinkIdList', (_message.Message,), { + 'DESCRIPTOR' : _LINKIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkIdList) + }) +_sym_db.RegisterMessage(LinkIdList) + +LinkList = _reflection.GeneratedProtocolMessageType('LinkList', (_message.Message,), { + 'DESCRIPTOR' : _LINKLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkList) + }) +_sym_db.RegisterMessage(LinkList) + +LinkEvent = _reflection.GeneratedProtocolMessageType('LinkEvent', (_message.Message,), { + 'DESCRIPTOR' : _LINKEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkEvent) + }) +_sym_db.RegisterMessage(LinkEvent) + +ServiceId = _reflection.GeneratedProtocolMessageType('ServiceId', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceId) + }) +_sym_db.RegisterMessage(ServiceId) + +Service = _reflection.GeneratedProtocolMessageType('Service', (_message.Message,), { + 'DESCRIPTOR' : _SERVICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Service) + }) +_sym_db.RegisterMessage(Service) + +ServiceStatus = _reflection.GeneratedProtocolMessageType('ServiceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SERVICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceStatus) + }) +_sym_db.RegisterMessage(ServiceStatus) + +ServiceConfig = _reflection.GeneratedProtocolMessageType('ServiceConfig', (_message.Message,), { + 'DESCRIPTOR' : _SERVICECONFIG, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceConfig) + }) +_sym_db.RegisterMessage(ServiceConfig) + +ServiceIdList = _reflection.GeneratedProtocolMessageType('ServiceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceIdList) + }) +_sym_db.RegisterMessage(ServiceIdList) + +ServiceList = _reflection.GeneratedProtocolMessageType('ServiceList', (_message.Message,), { + 'DESCRIPTOR' : _SERVICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceList) + }) +_sym_db.RegisterMessage(ServiceList) + +ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceEvent) + }) +_sym_db.RegisterMessage(ServiceEvent) + +SliceId = _reflection.GeneratedProtocolMessageType('SliceId', (_message.Message,), { + 'DESCRIPTOR' : _SLICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceId) + }) +_sym_db.RegisterMessage(SliceId) + +Slice = _reflection.GeneratedProtocolMessageType('Slice', (_message.Message,), { + 'DESCRIPTOR' : _SLICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Slice) + }) +_sym_db.RegisterMessage(Slice) + +SliceStatus = _reflection.GeneratedProtocolMessageType('SliceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SLICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceStatus) + }) +_sym_db.RegisterMessage(SliceStatus) + +SliceIdList = _reflection.GeneratedProtocolMessageType('SliceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SLICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceIdList) + }) +_sym_db.RegisterMessage(SliceIdList) + +SliceList = _reflection.GeneratedProtocolMessageType('SliceList', (_message.Message,), { + 'DESCRIPTOR' : _SLICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceList) + }) +_sym_db.RegisterMessage(SliceList) + +SliceEvent = _reflection.GeneratedProtocolMessageType('SliceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SLICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceEvent) + }) +_sym_db.RegisterMessage(SliceEvent) + +ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionId) + }) +_sym_db.RegisterMessage(ConnectionId) + +Connection = _reflection.GeneratedProtocolMessageType('Connection', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTION, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Connection) + }) +_sym_db.RegisterMessage(Connection) + +ConnectionIdList = _reflection.GeneratedProtocolMessageType('ConnectionIdList', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionIdList) + }) +_sym_db.RegisterMessage(ConnectionIdList) + +ConnectionList = _reflection.GeneratedProtocolMessageType('ConnectionList', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionList) + }) +_sym_db.RegisterMessage(ConnectionList) + +ConnectionEvent = _reflection.GeneratedProtocolMessageType('ConnectionEvent', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionEvent) + }) +_sym_db.RegisterMessage(ConnectionEvent) + +EndPointId = _reflection.GeneratedProtocolMessageType('EndPointId', (_message.Message,), { + 'DESCRIPTOR' : _ENDPOINTID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.EndPointId) + }) +_sym_db.RegisterMessage(EndPointId) + +EndPoint = _reflection.GeneratedProtocolMessageType('EndPoint', (_message.Message,), { + 'DESCRIPTOR' : _ENDPOINT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.EndPoint) + }) +_sym_db.RegisterMessage(EndPoint) + +ConfigRule = _reflection.GeneratedProtocolMessageType('ConfigRule', (_message.Message,), { + 'DESCRIPTOR' : _CONFIGRULE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConfigRule) + }) +_sym_db.RegisterMessage(ConfigRule) + +Constraint = _reflection.GeneratedProtocolMessageType('Constraint', (_message.Message,), { + 'DESCRIPTOR' : _CONSTRAINT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Constraint) + }) +_sym_db.RegisterMessage(Constraint) + +TeraFlowController = _reflection.GeneratedProtocolMessageType('TeraFlowController', (_message.Message,), { + 'DESCRIPTOR' : _TERAFLOWCONTROLLER, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TeraFlowController) + }) +_sym_db.RegisterMessage(TeraFlowController) + +AuthenticationResult = _reflection.GeneratedProtocolMessageType('AuthenticationResult', (_message.Message,), { + 'DESCRIPTOR' : _AUTHENTICATIONRESULT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.AuthenticationResult) + }) +_sym_db.RegisterMessage(AuthenticationResult) + + + +_CONTEXTSERVICE = _descriptor.ServiceDescriptor( + name='ContextService', + full_name='context.ContextService', + file=DESCRIPTOR, + index=0, + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_start=5273, + serialized_end=7688, + methods=[ + _descriptor.MethodDescriptor( + name='ListContextIds', + full_name='context.ContextService.ListContextIds', + index=0, + containing_service=None, + input_type=_EMPTY, + output_type=_CONTEXTIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListContexts', + full_name='context.ContextService.ListContexts', + index=1, + containing_service=None, + input_type=_EMPTY, + output_type=_CONTEXTLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetContext', + full_name='context.ContextService.GetContext', + index=2, + containing_service=None, + input_type=_CONTEXTID, + output_type=_CONTEXT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetContext', + full_name='context.ContextService.SetContext', + index=3, + containing_service=None, + input_type=_CONTEXT, + output_type=_CONTEXTID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveContext', + full_name='context.ContextService.RemoveContext', + index=4, + containing_service=None, + input_type=_CONTEXTID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetContextEvents', + full_name='context.ContextService.GetContextEvents', + index=5, + containing_service=None, + input_type=_EMPTY, + output_type=_CONTEXTEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListTopologyIds', + full_name='context.ContextService.ListTopologyIds', + index=6, + containing_service=None, + input_type=_CONTEXTID, + output_type=_TOPOLOGYIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListTopologies', + full_name='context.ContextService.ListTopologies', + index=7, + containing_service=None, + input_type=_CONTEXTID, + output_type=_TOPOLOGYLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetTopology', + full_name='context.ContextService.GetTopology', + index=8, + containing_service=None, + input_type=_TOPOLOGYID, + output_type=_TOPOLOGY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetTopology', + full_name='context.ContextService.SetTopology', + index=9, + containing_service=None, + input_type=_TOPOLOGY, + output_type=_TOPOLOGYID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveTopology', + full_name='context.ContextService.RemoveTopology', + index=10, + containing_service=None, + input_type=_TOPOLOGYID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetTopologyEvents', + full_name='context.ContextService.GetTopologyEvents', + index=11, + containing_service=None, + input_type=_EMPTY, + output_type=_TOPOLOGYEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListDeviceIds', + full_name='context.ContextService.ListDeviceIds', + index=12, + containing_service=None, + input_type=_EMPTY, + output_type=_DEVICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListDevices', + full_name='context.ContextService.ListDevices', + index=13, + containing_service=None, + input_type=_EMPTY, + output_type=_DEVICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetDevice', + full_name='context.ContextService.GetDevice', + index=14, + containing_service=None, + input_type=_DEVICEID, + output_type=_DEVICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetDevice', + full_name='context.ContextService.SetDevice', + index=15, + containing_service=None, + input_type=_DEVICE, + output_type=_DEVICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveDevice', + full_name='context.ContextService.RemoveDevice', + index=16, + containing_service=None, + input_type=_DEVICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetDeviceEvents', + full_name='context.ContextService.GetDeviceEvents', + index=17, + containing_service=None, + input_type=_EMPTY, + output_type=_DEVICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListLinkIds', + full_name='context.ContextService.ListLinkIds', + index=18, + containing_service=None, + input_type=_EMPTY, + output_type=_LINKIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListLinks', + full_name='context.ContextService.ListLinks', + index=19, + containing_service=None, + input_type=_EMPTY, + output_type=_LINKLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetLink', + full_name='context.ContextService.GetLink', + index=20, + containing_service=None, + input_type=_LINKID, + output_type=_LINK, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetLink', + full_name='context.ContextService.SetLink', + index=21, + containing_service=None, + input_type=_LINK, + output_type=_LINKID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveLink', + full_name='context.ContextService.RemoveLink', + index=22, + containing_service=None, + input_type=_LINKID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetLinkEvents', + full_name='context.ContextService.GetLinkEvents', + index=23, + containing_service=None, + input_type=_EMPTY, + output_type=_LINKEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListServiceIds', + full_name='context.ContextService.ListServiceIds', + index=24, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SERVICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListServices', + full_name='context.ContextService.ListServices', + index=25, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SERVICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetService', + full_name='context.ContextService.GetService', + index=26, + containing_service=None, + input_type=_SERVICEID, + output_type=_SERVICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetService', + full_name='context.ContextService.SetService', + index=27, + containing_service=None, + input_type=_SERVICE, + output_type=_SERVICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveService', + full_name='context.ContextService.RemoveService', + index=28, + containing_service=None, + input_type=_SERVICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetServiceEvents', + full_name='context.ContextService.GetServiceEvents', + index=29, + containing_service=None, + input_type=_EMPTY, + output_type=_SERVICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSliceIds', + full_name='context.ContextService.ListSliceIds', + index=30, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSlices', + full_name='context.ContextService.ListSlices', + index=31, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSlice', + full_name='context.ContextService.GetSlice', + index=32, + containing_service=None, + input_type=_SLICEID, + output_type=_SLICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetSlice', + full_name='context.ContextService.SetSlice', + index=33, + containing_service=None, + input_type=_SLICE, + output_type=_SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveSlice', + full_name='context.ContextService.RemoveSlice', + index=34, + containing_service=None, + input_type=_SLICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSliceEvents', + full_name='context.ContextService.GetSliceEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_SLICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListConnectionIds', + full_name='context.ContextService.ListConnectionIds', + index=36, + containing_service=None, + input_type=_SERVICEID, + output_type=_CONNECTIONIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListConnections', + full_name='context.ContextService.ListConnections', + index=37, + containing_service=None, + input_type=_SERVICEID, + output_type=_CONNECTIONLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetConnection', + full_name='context.ContextService.GetConnection', + index=38, + containing_service=None, + input_type=_CONNECTIONID, + output_type=_CONNECTION, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetConnection', + full_name='context.ContextService.SetConnection', + index=39, + containing_service=None, + input_type=_CONNECTION, + output_type=_CONNECTIONID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveConnection', + full_name='context.ContextService.RemoveConnection', + index=40, + containing_service=None, + input_type=_CONNECTIONID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetConnectionEvents', + full_name='context.ContextService.GetConnectionEvents', + index=41, + containing_service=None, + input_type=_EMPTY, + output_type=_CONNECTIONEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), +]) +_sym_db.RegisterServiceDescriptor(_CONTEXTSERVICE) + +DESCRIPTOR.services_by_name['ContextService'] = _CONTEXTSERVICE + +# @@protoc_insertion_point(module_scope) -- GitLab From 6cf5ce13e0b3891f979fe21902a69f1e55bad2b0 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 12:58:41 +0200 Subject: [PATCH 032/353] Corrected genproto script for l3_centralizedattackdetector. --- src/l3_centralizedattackdetector/genproto.sh | 8 +- .../proto/context_pb2.py | 3071 +++++++++++++++++ .../proto/l3_attackmitigator_pb2_grpc.py | 102 - 3 files changed, 3077 insertions(+), 104 deletions(-) create mode 100644 src/l3_centralizedattackdetector/proto/context_pb2.py delete mode 100644 src/l3_centralizedattackdetector/proto/l3_attackmitigator_pb2_grpc.py diff --git a/src/l3_centralizedattackdetector/genproto.sh b/src/l3_centralizedattackdetector/genproto.sh index 4c78ef756..217994f32 100755 --- a/src/l3_centralizedattackdetector/genproto.sh +++ b/src/l3_centralizedattackdetector/genproto.sh @@ -36,10 +36,14 @@ tee proto/__init__.py << EOF > /dev/null EOF -python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto l3_centralizedattackdetector.proto +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 l3_attackmitigator.proto +python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto l3_centralizedattackdetector.proto + +rm proto/context_pb2_grpc.py +rm proto/l3_attackmitigator_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/l3_centralizedattackdetector_pb2.py sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/l3_centralizedattackdetector_pb2_grpc.py sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/l3_attackmitigator_pb2.py -sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/l3_attackmitigator_pb2_grpc.py diff --git a/src/l3_centralizedattackdetector/proto/context_pb2.py b/src/l3_centralizedattackdetector/proto/context_pb2.py new file mode 100644 index 000000000..50d501d3a --- /dev/null +++ b/src/l3_centralizedattackdetector/proto/context_pb2.py @@ -0,0 +1,3071 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: context.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 kpi_sample_types_pb2 as kpi__sample__types__pb2 + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='context.proto', + package='context', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\rcontext.proto\x12\x07\x63ontext\x1a\x16kpi_sample_types.proto\"\x07\n\x05\x45mpty\"\x14\n\x04Uuid\x12\x0c\n\x04uuid\x18\x01 \x01(\t\"F\n\x05\x45vent\x12\x11\n\ttimestamp\x18\x01 \x01(\x01\x12*\n\nevent_type\x18\x02 \x01(\x0e\x32\x16.context.EventTypeEnum\"0\n\tContextId\x12#\n\x0c\x63ontext_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xb6\x01\n\x07\x43ontext\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12)\n\x0ctopology_ids\x18\x02 \x03(\x0b\x32\x13.context.TopologyId\x12\'\n\x0bservice_ids\x18\x03 \x03(\x0b\x32\x12.context.ServiceId\x12/\n\ncontroller\x18\x04 \x01(\x0b\x32\x1b.context.TeraFlowController\"8\n\rContextIdList\x12\'\n\x0b\x63ontext_ids\x18\x01 \x03(\x0b\x32\x12.context.ContextId\"1\n\x0b\x43ontextList\x12\"\n\x08\x63ontexts\x18\x01 \x03(\x0b\x32\x10.context.Context\"U\n\x0c\x43ontextEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\ncontext_id\x18\x02 \x01(\x0b\x32\x12.context.ContextId\"Z\n\nTopologyId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12$\n\rtopology_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"~\n\x08Topology\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12%\n\ndevice_ids\x18\x02 \x03(\x0b\x32\x11.context.DeviceId\x12!\n\x08link_ids\x18\x03 \x03(\x0b\x32\x0f.context.LinkId\";\n\x0eTopologyIdList\x12)\n\x0ctopology_ids\x18\x01 \x03(\x0b\x32\x13.context.TopologyId\"5\n\x0cTopologyList\x12%\n\ntopologies\x18\x01 \x03(\x0b\x32\x11.context.Topology\"X\n\rTopologyEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12(\n\x0btopology_id\x18\x02 \x01(\x0b\x32\x13.context.TopologyId\".\n\x08\x44\x65viceId\x12\"\n\x0b\x64\x65vice_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x9a\x02\n\x06\x44\x65vice\x12$\n\tdevice_id\x18\x01 \x01(\x0b\x32\x11.context.DeviceId\x12\x13\n\x0b\x64\x65vice_type\x18\x02 \x01(\t\x12,\n\rdevice_config\x18\x03 \x01(\x0b\x32\x15.context.DeviceConfig\x12G\n\x19\x64\x65vice_operational_status\x18\x04 \x01(\x0e\x32$.context.DeviceOperationalStatusEnum\x12\x31\n\x0e\x64\x65vice_drivers\x18\x05 \x03(\x0e\x32\x19.context.DeviceDriverEnum\x12+\n\x10\x64\x65vice_endpoints\x18\x06 \x03(\x0b\x32\x11.context.EndPoint\"9\n\x0c\x44\x65viceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"5\n\x0c\x44\x65viceIdList\x12%\n\ndevice_ids\x18\x01 \x03(\x0b\x32\x11.context.DeviceId\".\n\nDeviceList\x12 \n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x0f.context.Device\"R\n\x0b\x44\x65viceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\"*\n\x06LinkId\x12 \n\tlink_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"X\n\x04Link\x12 \n\x07link_id\x18\x01 \x01(\x0b\x32\x0f.context.LinkId\x12.\n\x11link_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\"/\n\nLinkIdList\x12!\n\x08link_ids\x18\x01 \x03(\x0b\x32\x0f.context.LinkId\"(\n\x08LinkList\x12\x1c\n\x05links\x18\x01 \x03(\x0b\x32\r.context.Link\"L\n\tLinkEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12 \n\x07link_id\x18\x02 \x01(\x0b\x32\x0f.context.LinkId\"X\n\tServiceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12#\n\x0cservice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\xa6\x02\n\x07Service\x12&\n\nservice_id\x18\x01 \x01(\x0b\x32\x12.context.ServiceId\x12.\n\x0cservice_type\x18\x02 \x01(\x0e\x32\x18.context.ServiceTypeEnum\x12\x31\n\x14service_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12\x30\n\x13service_constraints\x18\x04 \x03(\x0b\x32\x13.context.Constraint\x12.\n\x0eservice_status\x18\x05 \x01(\x0b\x32\x16.context.ServiceStatus\x12.\n\x0eservice_config\x18\x06 \x01(\x0b\x32\x16.context.ServiceConfig\"C\n\rServiceStatus\x12\x32\n\x0eservice_status\x18\x01 \x01(\x0e\x32\x1a.context.ServiceStatusEnum\":\n\rServiceConfig\x12)\n\x0c\x63onfig_rules\x18\x01 \x03(\x0b\x32\x13.context.ConfigRule\"8\n\rServiceIdList\x12\'\n\x0bservice_ids\x18\x01 \x03(\x0b\x32\x12.context.ServiceId\"1\n\x0bServiceList\x12\"\n\x08services\x18\x01 \x03(\x0b\x32\x10.context.Service\"U\n\x0cServiceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\"T\n\x07SliceId\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12!\n\nslice_uuid\x18\x02 \x01(\x0b\x32\r.context.Uuid\"\x95\x02\n\x05Slice\x12\"\n\x08slice_id\x18\x01 \x01(\x0b\x32\x10.context.SliceId\x12/\n\x12slice_endpoint_ids\x18\x02 \x03(\x0b\x32\x13.context.EndPointId\x12.\n\x11slice_constraints\x18\x03 \x03(\x0b\x32\x13.context.Constraint\x12-\n\x11slice_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\x12,\n\x12slice_subslice_ids\x18\x05 \x03(\x0b\x32\x10.context.SliceId\x12*\n\x0cslice_status\x18\x06 \x01(\x0b\x32\x14.context.SliceStatus\"=\n\x0bSliceStatus\x12.\n\x0cslice_status\x18\x01 \x01(\x0e\x32\x18.context.SliceStatusEnum\"2\n\x0bSliceIdList\x12#\n\tslice_ids\x18\x01 \x03(\x0b\x32\x10.context.SliceId\"+\n\tSliceList\x12\x1e\n\x06slices\x18\x01 \x03(\x0b\x32\x0e.context.Slice\"O\n\nSliceEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12\"\n\x08slice_id\x18\x02 \x01(\x0b\x32\x10.context.SliceId\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xc4\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12&\n\nservice_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12\x33\n\x16path_hops_endpoint_ids\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\x12+\n\x0fsub_service_ids\x18\x04 \x03(\x0b\x32\x12.context.ServiceId\"A\n\x10\x43onnectionIdList\x12-\n\x0e\x63onnection_ids\x18\x01 \x03(\x0b\x32\x15.context.ConnectionId\":\n\x0e\x43onnectionList\x12(\n\x0b\x63onnections\x18\x01 \x03(\x0b\x32\x13.context.Connection\"^\n\x0f\x43onnectionEvent\x12\x1d\n\x05\x65vent\x18\x01 \x01(\x0b\x32\x0e.context.Event\x12,\n\rconnection_id\x18\x02 \x01(\x0b\x32\x15.context.ConnectionId\"\x82\x01\n\nEndPointId\x12(\n\x0btopology_id\x18\x01 \x01(\x0b\x32\x13.context.TopologyId\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12$\n\rendpoint_uuid\x18\x03 \x01(\x0b\x32\r.context.Uuid\"\x86\x01\n\x08\x45ndPoint\x12(\n\x0b\x65ndpoint_id\x18\x01 \x01(\x0b\x32\x13.context.EndPointId\x12\x15\n\rendpoint_type\x18\x02 \x01(\t\x12\x39\n\x10kpi_sample_types\x18\x03 \x03(\x0e\x32\x1f.kpi_sample_types.KpiSampleType\"e\n\nConfigRule\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.context.ConfigActionEnum\x12\x14\n\x0cresource_key\x18\x02 \x01(\t\x12\x16\n\x0eresource_value\x18\x03 \x01(\t\"?\n\nConstraint\x12\x17\n\x0f\x63onstraint_type\x18\x01 \x01(\t\x12\x18\n\x10\x63onstraint_value\x18\x02 \x01(\t\"^\n\x12TeraFlowController\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x12\n\nip_address\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\r\"U\n\x14\x41uthenticationResult\x12&\n\ncontext_id\x18\x01 \x01(\x0b\x32\x12.context.ContextId\x12\x15\n\rauthenticated\x18\x02 \x01(\x08*j\n\rEventTypeEnum\x12\x17\n\x13\x45VENTTYPE_UNDEFINED\x10\x00\x12\x14\n\x10\x45VENTTYPE_CREATE\x10\x01\x12\x14\n\x10\x45VENTTYPE_UPDATE\x10\x02\x12\x14\n\x10\x45VENTTYPE_REMOVE\x10\x03*\xc5\x01\n\x10\x44\x65viceDriverEnum\x12\x1a\n\x16\x44\x45VICEDRIVER_UNDEFINED\x10\x00\x12\x1b\n\x17\x44\x45VICEDRIVER_OPENCONFIG\x10\x01\x12\x1e\n\x1a\x44\x45VICEDRIVER_TRANSPORT_API\x10\x02\x12\x13\n\x0f\x44\x45VICEDRIVER_P4\x10\x03\x12&\n\"DEVICEDRIVER_IETF_NETWORK_TOPOLOGY\x10\x04\x12\x1b\n\x17\x44\x45VICEDRIVER_ONF_TR_352\x10\x05*\x8f\x01\n\x1b\x44\x65viceOperationalStatusEnum\x12%\n!DEVICEOPERATIONALSTATUS_UNDEFINED\x10\x00\x12$\n DEVICEOPERATIONALSTATUS_DISABLED\x10\x01\x12#\n\x1f\x44\x45VICEOPERATIONALSTATUS_ENABLED\x10\x02*\x81\x01\n\x0fServiceTypeEnum\x12\x17\n\x13SERVICETYPE_UNKNOWN\x10\x00\x12\x14\n\x10SERVICETYPE_L3NM\x10\x01\x12\x14\n\x10SERVICETYPE_L2NM\x10\x02\x12)\n%SERVICETYPE_TAPI_CONNECTIVITY_SERVICE\x10\x03*\x88\x01\n\x11ServiceStatusEnum\x12\x1b\n\x17SERVICESTATUS_UNDEFINED\x10\x00\x12\x19\n\x15SERVICESTATUS_PLANNED\x10\x01\x12\x18\n\x14SERVICESTATUS_ACTIVE\x10\x02\x12!\n\x1dSERVICESTATUS_PENDING_REMOVAL\x10\x03*\x8b\x01\n\x0fSliceStatusEnum\x12\x19\n\x15SLICESTATUS_UNDEFINED\x10\x00\x12\x17\n\x13SLICESTATUS_PLANNED\x10\x01\x12\x14\n\x10SLICESTATUS_INIT\x10\x02\x12\x16\n\x12SLICESTATUS_ACTIVE\x10\x03\x12\x16\n\x12SLICESTATUS_DEINIT\x10\x04*]\n\x10\x43onfigActionEnum\x12\x1a\n\x16\x43ONFIGACTION_UNDEFINED\x10\x00\x12\x14\n\x10\x43ONFIGACTION_SET\x10\x01\x12\x17\n\x13\x43ONFIGACTION_DELETE\x10\x02\x32\xef\x12\n\x0e\x43ontextService\x12:\n\x0eListContextIds\x12\x0e.context.Empty\x1a\x16.context.ContextIdList\"\x00\x12\x36\n\x0cListContexts\x12\x0e.context.Empty\x1a\x14.context.ContextList\"\x00\x12\x34\n\nGetContext\x12\x12.context.ContextId\x1a\x10.context.Context\"\x00\x12\x34\n\nSetContext\x12\x10.context.Context\x1a\x12.context.ContextId\"\x00\x12\x35\n\rRemoveContext\x12\x12.context.ContextId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetContextEvents\x12\x0e.context.Empty\x1a\x15.context.ContextEvent\"\x00\x30\x01\x12@\n\x0fListTopologyIds\x12\x12.context.ContextId\x1a\x17.context.TopologyIdList\"\x00\x12=\n\x0eListTopologies\x12\x12.context.ContextId\x1a\x15.context.TopologyList\"\x00\x12\x37\n\x0bGetTopology\x12\x13.context.TopologyId\x1a\x11.context.Topology\"\x00\x12\x37\n\x0bSetTopology\x12\x11.context.Topology\x1a\x13.context.TopologyId\"\x00\x12\x37\n\x0eRemoveTopology\x12\x13.context.TopologyId\x1a\x0e.context.Empty\"\x00\x12?\n\x11GetTopologyEvents\x12\x0e.context.Empty\x1a\x16.context.TopologyEvent\"\x00\x30\x01\x12\x38\n\rListDeviceIds\x12\x0e.context.Empty\x1a\x15.context.DeviceIdList\"\x00\x12\x34\n\x0bListDevices\x12\x0e.context.Empty\x1a\x13.context.DeviceList\"\x00\x12\x31\n\tGetDevice\x12\x11.context.DeviceId\x1a\x0f.context.Device\"\x00\x12\x31\n\tSetDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0cRemoveDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12;\n\x0fGetDeviceEvents\x12\x0e.context.Empty\x1a\x14.context.DeviceEvent\"\x00\x30\x01\x12\x34\n\x0bListLinkIds\x12\x0e.context.Empty\x1a\x13.context.LinkIdList\"\x00\x12\x30\n\tListLinks\x12\x0e.context.Empty\x1a\x11.context.LinkList\"\x00\x12+\n\x07GetLink\x12\x0f.context.LinkId\x1a\r.context.Link\"\x00\x12+\n\x07SetLink\x12\r.context.Link\x1a\x0f.context.LinkId\"\x00\x12/\n\nRemoveLink\x12\x0f.context.LinkId\x1a\x0e.context.Empty\"\x00\x12\x37\n\rGetLinkEvents\x12\x0e.context.Empty\x1a\x12.context.LinkEvent\"\x00\x30\x01\x12>\n\x0eListServiceIds\x12\x12.context.ContextId\x1a\x16.context.ServiceIdList\"\x00\x12:\n\x0cListServices\x12\x12.context.ContextId\x1a\x14.context.ServiceList\"\x00\x12\x34\n\nGetService\x12\x12.context.ServiceId\x1a\x10.context.Service\"\x00\x12\x34\n\nSetService\x12\x10.context.Service\x1a\x12.context.ServiceId\"\x00\x12\x35\n\rRemoveService\x12\x12.context.ServiceId\x1a\x0e.context.Empty\"\x00\x12=\n\x10GetServiceEvents\x12\x0e.context.Empty\x1a\x15.context.ServiceEvent\"\x00\x30\x01\x12:\n\x0cListSliceIds\x12\x12.context.ContextId\x1a\x14.context.SliceIdList\"\x00\x12\x36\n\nListSlices\x12\x12.context.ContextId\x1a\x12.context.SliceList\"\x00\x12.\n\x08GetSlice\x12\x10.context.SliceId\x1a\x0e.context.Slice\"\x00\x12.\n\x08SetSlice\x12\x0e.context.Slice\x1a\x10.context.SliceId\"\x00\x12\x31\n\x0bRemoveSlice\x12\x10.context.SliceId\x1a\x0e.context.Empty\"\x00\x12\x39\n\x0eGetSliceEvents\x12\x0e.context.Empty\x1a\x13.context.SliceEvent\"\x00\x30\x01\x12\x44\n\x11ListConnectionIds\x12\x12.context.ServiceId\x1a\x19.context.ConnectionIdList\"\x00\x12@\n\x0fListConnections\x12\x12.context.ServiceId\x1a\x17.context.ConnectionList\"\x00\x12=\n\rGetConnection\x12\x15.context.ConnectionId\x1a\x13.context.Connection\"\x00\x12=\n\rSetConnection\x12\x13.context.Connection\x1a\x15.context.ConnectionId\"\x00\x12;\n\x10RemoveConnection\x12\x15.context.ConnectionId\x1a\x0e.context.Empty\"\x00\x12\x43\n\x13GetConnectionEvents\x12\x0e.context.Empty\x1a\x18.context.ConnectionEvent\"\x00\x30\x01\x62\x06proto3' + , + dependencies=[kpi__sample__types__pb2.DESCRIPTOR,]) + +_EVENTTYPEENUM = _descriptor.EnumDescriptor( + name='EventTypeEnum', + full_name='context.EventTypeEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_CREATE', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_UPDATE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EVENTTYPE_REMOVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4310, + serialized_end=4416, +) +_sym_db.RegisterEnumDescriptor(_EVENTTYPEENUM) + +EventTypeEnum = enum_type_wrapper.EnumTypeWrapper(_EVENTTYPEENUM) +_DEVICEDRIVERENUM = _descriptor.EnumDescriptor( + name='DeviceDriverEnum', + full_name='context.DeviceDriverEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_OPENCONFIG', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_TRANSPORT_API', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_P4', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_IETF_NETWORK_TOPOLOGY', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEDRIVER_ONF_TR_352', index=5, number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4419, + serialized_end=4616, +) +_sym_db.RegisterEnumDescriptor(_DEVICEDRIVERENUM) + +DeviceDriverEnum = enum_type_wrapper.EnumTypeWrapper(_DEVICEDRIVERENUM) +_DEVICEOPERATIONALSTATUSENUM = _descriptor.EnumDescriptor( + name='DeviceOperationalStatusEnum', + full_name='context.DeviceOperationalStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DEVICEOPERATIONALSTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEOPERATIONALSTATUS_DISABLED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICEOPERATIONALSTATUS_ENABLED', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4619, + serialized_end=4762, +) +_sym_db.RegisterEnumDescriptor(_DEVICEOPERATIONALSTATUSENUM) + +DeviceOperationalStatusEnum = enum_type_wrapper.EnumTypeWrapper(_DEVICEOPERATIONALSTATUSENUM) +_SERVICETYPEENUM = _descriptor.EnumDescriptor( + name='ServiceTypeEnum', + full_name='context.ServiceTypeEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_L3NM', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_L2NM', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICETYPE_TAPI_CONNECTIVITY_SERVICE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4765, + serialized_end=4894, +) +_sym_db.RegisterEnumDescriptor(_SERVICETYPEENUM) + +ServiceTypeEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICETYPEENUM) +_SERVICESTATUSENUM = _descriptor.EnumDescriptor( + name='ServiceStatusEnum', + full_name='context.ServiceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_ACTIVE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SERVICESTATUS_PENDING_REMOVAL', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4897, + serialized_end=5033, +) +_sym_db.RegisterEnumDescriptor(_SERVICESTATUSENUM) + +ServiceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SERVICESTATUSENUM) +_SLICESTATUSENUM = _descriptor.EnumDescriptor( + name='SliceStatusEnum', + full_name='context.SliceStatusEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_PLANNED', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_INIT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_ACTIVE', index=3, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SLICESTATUS_DEINIT', index=4, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5036, + serialized_end=5175, +) +_sym_db.RegisterEnumDescriptor(_SLICESTATUSENUM) + +SliceStatusEnum = enum_type_wrapper.EnumTypeWrapper(_SLICESTATUSENUM) +_CONFIGACTIONENUM = _descriptor.EnumDescriptor( + name='ConfigActionEnum', + full_name='context.ConfigActionEnum', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='CONFIGACTION_UNDEFINED', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CONFIGACTION_SET', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CONFIGACTION_DELETE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5177, + serialized_end=5270, +) +_sym_db.RegisterEnumDescriptor(_CONFIGACTIONENUM) + +ConfigActionEnum = enum_type_wrapper.EnumTypeWrapper(_CONFIGACTIONENUM) +EVENTTYPE_UNDEFINED = 0 +EVENTTYPE_CREATE = 1 +EVENTTYPE_UPDATE = 2 +EVENTTYPE_REMOVE = 3 +DEVICEDRIVER_UNDEFINED = 0 +DEVICEDRIVER_OPENCONFIG = 1 +DEVICEDRIVER_TRANSPORT_API = 2 +DEVICEDRIVER_P4 = 3 +DEVICEDRIVER_IETF_NETWORK_TOPOLOGY = 4 +DEVICEDRIVER_ONF_TR_352 = 5 +DEVICEOPERATIONALSTATUS_UNDEFINED = 0 +DEVICEOPERATIONALSTATUS_DISABLED = 1 +DEVICEOPERATIONALSTATUS_ENABLED = 2 +SERVICETYPE_UNKNOWN = 0 +SERVICETYPE_L3NM = 1 +SERVICETYPE_L2NM = 2 +SERVICETYPE_TAPI_CONNECTIVITY_SERVICE = 3 +SERVICESTATUS_UNDEFINED = 0 +SERVICESTATUS_PLANNED = 1 +SERVICESTATUS_ACTIVE = 2 +SERVICESTATUS_PENDING_REMOVAL = 3 +SLICESTATUS_UNDEFINED = 0 +SLICESTATUS_PLANNED = 1 +SLICESTATUS_INIT = 2 +SLICESTATUS_ACTIVE = 3 +SLICESTATUS_DEINIT = 4 +CONFIGACTION_UNDEFINED = 0 +CONFIGACTION_SET = 1 +CONFIGACTION_DELETE = 2 + + + +_EMPTY = _descriptor.Descriptor( + name='Empty', + full_name='context.Empty', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=50, + serialized_end=57, +) + + +_UUID = _descriptor.Descriptor( + name='Uuid', + full_name='context.Uuid', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='uuid', full_name='context.Uuid.uuid', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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=59, + serialized_end=79, +) + + +_EVENT = _descriptor.Descriptor( + name='Event', + full_name='context.Event', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='timestamp', full_name='context.Event.timestamp', index=0, + number=1, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(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), + _descriptor.FieldDescriptor( + name='event_type', full_name='context.Event.event_type', 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=81, + serialized_end=151, +) + + +_CONTEXTID = _descriptor.Descriptor( + name='ContextId', + full_name='context.ContextId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_uuid', full_name='context.ContextId.context_uuid', 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=153, + serialized_end=201, +) + + +_CONTEXT = _descriptor.Descriptor( + name='Context', + full_name='context.Context', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.Context.context_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='topology_ids', full_name='context.Context.topology_ids', 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='service_ids', full_name='context.Context.service_ids', 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='controller', full_name='context.Context.controller', index=3, + number=4, 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=204, + serialized_end=386, +) + + +_CONTEXTIDLIST = _descriptor.Descriptor( + name='ContextIdList', + full_name='context.ContextIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_ids', full_name='context.ContextIdList.context_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=388, + serialized_end=444, +) + + +_CONTEXTLIST = _descriptor.Descriptor( + name='ContextList', + full_name='context.ContextList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='contexts', full_name='context.ContextList.contexts', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=446, + serialized_end=495, +) + + +_CONTEXTEVENT = _descriptor.Descriptor( + name='ContextEvent', + full_name='context.ContextEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.ContextEvent.event', 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='context_id', full_name='context.ContextEvent.context_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=497, + serialized_end=582, +) + + +_TOPOLOGYID = _descriptor.Descriptor( + name='TopologyId', + full_name='context.TopologyId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.TopologyId.context_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='topology_uuid', full_name='context.TopologyId.topology_uuid', 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=584, + serialized_end=674, +) + + +_TOPOLOGY = _descriptor.Descriptor( + name='Topology', + full_name='context.Topology', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topology_id', full_name='context.Topology.topology_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='device_ids', full_name='context.Topology.device_ids', 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='link_ids', full_name='context.Topology.link_ids', 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=676, + serialized_end=802, +) + + +_TOPOLOGYIDLIST = _descriptor.Descriptor( + name='TopologyIdList', + full_name='context.TopologyIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topology_ids', full_name='context.TopologyIdList.topology_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=804, + serialized_end=863, +) + + +_TOPOLOGYLIST = _descriptor.Descriptor( + name='TopologyList', + full_name='context.TopologyList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topologies', full_name='context.TopologyList.topologies', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=865, + serialized_end=918, +) + + +_TOPOLOGYEVENT = _descriptor.Descriptor( + name='TopologyEvent', + full_name='context.TopologyEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.TopologyEvent.event', 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='topology_id', full_name='context.TopologyEvent.topology_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=920, + serialized_end=1008, +) + + +_DEVICEID = _descriptor.Descriptor( + name='DeviceId', + full_name='context.DeviceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='device_uuid', full_name='context.DeviceId.device_uuid', 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=1010, + serialized_end=1056, +) + + +_DEVICE = _descriptor.Descriptor( + name='Device', + full_name='context.Device', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='device_id', full_name='context.Device.device_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='device_type', full_name='context.Device.device_type', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='device_config', full_name='context.Device.device_config', index=2, + number=3, 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='device_operational_status', full_name='context.Device.device_operational_status', index=3, + number=4, 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), + _descriptor.FieldDescriptor( + name='device_drivers', full_name='context.Device.device_drivers', index=4, + number=5, type=14, cpp_type=8, 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='device_endpoints', full_name='context.Device.device_endpoints', index=5, + number=6, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1059, + serialized_end=1341, +) + + +_DEVICECONFIG = _descriptor.Descriptor( + name='DeviceConfig', + full_name='context.DeviceConfig', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='config_rules', full_name='context.DeviceConfig.config_rules', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1343, + serialized_end=1400, +) + + +_DEVICEIDLIST = _descriptor.Descriptor( + name='DeviceIdList', + full_name='context.DeviceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='device_ids', full_name='context.DeviceIdList.device_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1402, + serialized_end=1455, +) + + +_DEVICELIST = _descriptor.Descriptor( + name='DeviceList', + full_name='context.DeviceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='devices', full_name='context.DeviceList.devices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1457, + serialized_end=1503, +) + + +_DEVICEEVENT = _descriptor.Descriptor( + name='DeviceEvent', + full_name='context.DeviceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.DeviceEvent.event', 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='device_id', full_name='context.DeviceEvent.device_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=1505, + serialized_end=1587, +) + + +_LINKID = _descriptor.Descriptor( + name='LinkId', + full_name='context.LinkId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='link_uuid', full_name='context.LinkId.link_uuid', 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=1589, + serialized_end=1631, +) + + +_LINK = _descriptor.Descriptor( + name='Link', + full_name='context.Link', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='link_id', full_name='context.Link.link_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='link_endpoint_ids', full_name='context.Link.link_endpoint_ids', 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1633, + serialized_end=1721, +) + + +_LINKIDLIST = _descriptor.Descriptor( + name='LinkIdList', + full_name='context.LinkIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='link_ids', full_name='context.LinkIdList.link_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1723, + serialized_end=1770, +) + + +_LINKLIST = _descriptor.Descriptor( + name='LinkList', + full_name='context.LinkList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='links', full_name='context.LinkList.links', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=1772, + serialized_end=1812, +) + + +_LINKEVENT = _descriptor.Descriptor( + name='LinkEvent', + full_name='context.LinkEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.LinkEvent.event', 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='link_id', full_name='context.LinkEvent.link_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=1814, + serialized_end=1890, +) + + +_SERVICEID = _descriptor.Descriptor( + name='ServiceId', + full_name='context.ServiceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.ServiceId.context_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='service_uuid', full_name='context.ServiceId.service_uuid', 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=1892, + serialized_end=1980, +) + + +_SERVICE = _descriptor.Descriptor( + name='Service', + full_name='context.Service', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='service_id', full_name='context.Service.service_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='service_type', full_name='context.Service.service_type', 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), + _descriptor.FieldDescriptor( + name='service_endpoint_ids', full_name='context.Service.service_endpoint_ids', 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='service_constraints', full_name='context.Service.service_constraints', 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='service_status', full_name='context.Service.service_status', index=4, + number=5, 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='service_config', full_name='context.Service.service_config', 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=1983, + serialized_end=2277, +) + + +_SERVICESTATUS = _descriptor.Descriptor( + name='ServiceStatus', + full_name='context.ServiceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='service_status', full_name='context.ServiceStatus.service_status', index=0, + number=1, 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=2279, + serialized_end=2346, +) + + +_SERVICECONFIG = _descriptor.Descriptor( + name='ServiceConfig', + full_name='context.ServiceConfig', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='config_rules', full_name='context.ServiceConfig.config_rules', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2348, + serialized_end=2406, +) + + +_SERVICEIDLIST = _descriptor.Descriptor( + name='ServiceIdList', + full_name='context.ServiceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='service_ids', full_name='context.ServiceIdList.service_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2408, + serialized_end=2464, +) + + +_SERVICELIST = _descriptor.Descriptor( + name='ServiceList', + full_name='context.ServiceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='services', full_name='context.ServiceList.services', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2466, + serialized_end=2515, +) + + +_SERVICEEVENT = _descriptor.Descriptor( + name='ServiceEvent', + full_name='context.ServiceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.ServiceEvent.event', 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='service_id', full_name='context.ServiceEvent.service_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=2517, + serialized_end=2602, +) + + +_SLICEID = _descriptor.Descriptor( + name='SliceId', + full_name='context.SliceId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.SliceId.context_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='slice_uuid', full_name='context.SliceId.slice_uuid', 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=2604, + serialized_end=2688, +) + + +_SLICE = _descriptor.Descriptor( + name='Slice', + full_name='context.Slice', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_id', full_name='context.Slice.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='slice_endpoint_ids', full_name='context.Slice.slice_endpoint_ids', 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='slice_constraints', full_name='context.Slice.slice_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='slice_service_ids', full_name='context.Slice.slice_service_ids', 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='slice_subslice_ids', full_name='context.Slice.slice_subslice_ids', 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='slice_status', full_name='context.Slice.slice_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=2691, + serialized_end=2968, +) + + +_SLICESTATUS = _descriptor.Descriptor( + name='SliceStatus', + full_name='context.SliceStatus', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_status', full_name='context.SliceStatus.slice_status', index=0, + number=1, 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=2970, + serialized_end=3031, +) + + +_SLICEIDLIST = _descriptor.Descriptor( + name='SliceIdList', + full_name='context.SliceIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slice_ids', full_name='context.SliceIdList.slice_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3033, + serialized_end=3083, +) + + +_SLICELIST = _descriptor.Descriptor( + name='SliceList', + full_name='context.SliceList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='slices', full_name='context.SliceList.slices', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3085, + serialized_end=3128, +) + + +_SLICEEVENT = _descriptor.Descriptor( + name='SliceEvent', + full_name='context.SliceEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.SliceEvent.event', 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='context.SliceEvent.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=3130, + serialized_end=3209, +) + + +_CONNECTIONID = _descriptor.Descriptor( + name='ConnectionId', + full_name='context.ConnectionId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connection_uuid', full_name='context.ConnectionId.connection_uuid', 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=3211, + serialized_end=3265, +) + + +_CONNECTION = _descriptor.Descriptor( + name='Connection', + full_name='context.Connection', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connection_id', full_name='context.Connection.connection_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='service_id', full_name='context.Connection.service_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), + _descriptor.FieldDescriptor( + name='path_hops_endpoint_ids', full_name='context.Connection.path_hops_endpoint_ids', 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='sub_service_ids', full_name='context.Connection.sub_service_ids', 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3268, + serialized_end=3464, +) + + +_CONNECTIONIDLIST = _descriptor.Descriptor( + name='ConnectionIdList', + full_name='context.ConnectionIdList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connection_ids', full_name='context.ConnectionIdList.connection_ids', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3466, + serialized_end=3531, +) + + +_CONNECTIONLIST = _descriptor.Descriptor( + name='ConnectionList', + full_name='context.ConnectionList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='connections', full_name='context.ConnectionList.connections', index=0, + number=1, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3533, + serialized_end=3591, +) + + +_CONNECTIONEVENT = _descriptor.Descriptor( + name='ConnectionEvent', + full_name='context.ConnectionEvent', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='event', full_name='context.ConnectionEvent.event', 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='connection_id', full_name='context.ConnectionEvent.connection_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=3593, + serialized_end=3687, +) + + +_ENDPOINTID = _descriptor.Descriptor( + name='EndPointId', + full_name='context.EndPointId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='topology_id', full_name='context.EndPointId.topology_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='device_id', full_name='context.EndPointId.device_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), + _descriptor.FieldDescriptor( + name='endpoint_uuid', full_name='context.EndPointId.endpoint_uuid', index=2, + number=3, 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=3690, + serialized_end=3820, +) + + +_ENDPOINT = _descriptor.Descriptor( + name='EndPoint', + full_name='context.EndPoint', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='endpoint_id', full_name='context.EndPoint.endpoint_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='endpoint_type', full_name='context.EndPoint.endpoint_type', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='kpi_sample_types', full_name='context.EndPoint.kpi_sample_types', index=2, + number=3, type=14, cpp_type=8, 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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=3823, + serialized_end=3957, +) + + +_CONFIGRULE = _descriptor.Descriptor( + name='ConfigRule', + full_name='context.ConfigRule', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='action', full_name='context.ConfigRule.action', index=0, + number=1, 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), + _descriptor.FieldDescriptor( + name='resource_key', full_name='context.ConfigRule.resource_key', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='resource_value', full_name='context.ConfigRule.resource_value', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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=3959, + serialized_end=4060, +) + + +_CONSTRAINT = _descriptor.Descriptor( + name='Constraint', + full_name='context.Constraint', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='constraint_type', full_name='context.Constraint.constraint_type', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='constraint_value', full_name='context.Constraint.constraint_value', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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=4062, + serialized_end=4125, +) + + +_TERAFLOWCONTROLLER = _descriptor.Descriptor( + name='TeraFlowController', + full_name='context.TeraFlowController', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.TeraFlowController.context_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='ip_address', full_name='context.TeraFlowController.ip_address', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + 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='port', full_name='context.TeraFlowController.port', index=2, + number=3, type=13, cpp_type=3, 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=4127, + serialized_end=4221, +) + + +_AUTHENTICATIONRESULT = _descriptor.Descriptor( + name='AuthenticationResult', + full_name='context.AuthenticationResult', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='context_id', full_name='context.AuthenticationResult.context_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='authenticated', full_name='context.AuthenticationResult.authenticated', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + 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=4223, + serialized_end=4308, +) + +_EVENT.fields_by_name['event_type'].enum_type = _EVENTTYPEENUM +_CONTEXTID.fields_by_name['context_uuid'].message_type = _UUID +_CONTEXT.fields_by_name['context_id'].message_type = _CONTEXTID +_CONTEXT.fields_by_name['topology_ids'].message_type = _TOPOLOGYID +_CONTEXT.fields_by_name['service_ids'].message_type = _SERVICEID +_CONTEXT.fields_by_name['controller'].message_type = _TERAFLOWCONTROLLER +_CONTEXTIDLIST.fields_by_name['context_ids'].message_type = _CONTEXTID +_CONTEXTLIST.fields_by_name['contexts'].message_type = _CONTEXT +_CONTEXTEVENT.fields_by_name['event'].message_type = _EVENT +_CONTEXTEVENT.fields_by_name['context_id'].message_type = _CONTEXTID +_TOPOLOGYID.fields_by_name['context_id'].message_type = _CONTEXTID +_TOPOLOGYID.fields_by_name['topology_uuid'].message_type = _UUID +_TOPOLOGY.fields_by_name['topology_id'].message_type = _TOPOLOGYID +_TOPOLOGY.fields_by_name['device_ids'].message_type = _DEVICEID +_TOPOLOGY.fields_by_name['link_ids'].message_type = _LINKID +_TOPOLOGYIDLIST.fields_by_name['topology_ids'].message_type = _TOPOLOGYID +_TOPOLOGYLIST.fields_by_name['topologies'].message_type = _TOPOLOGY +_TOPOLOGYEVENT.fields_by_name['event'].message_type = _EVENT +_TOPOLOGYEVENT.fields_by_name['topology_id'].message_type = _TOPOLOGYID +_DEVICEID.fields_by_name['device_uuid'].message_type = _UUID +_DEVICE.fields_by_name['device_id'].message_type = _DEVICEID +_DEVICE.fields_by_name['device_config'].message_type = _DEVICECONFIG +_DEVICE.fields_by_name['device_operational_status'].enum_type = _DEVICEOPERATIONALSTATUSENUM +_DEVICE.fields_by_name['device_drivers'].enum_type = _DEVICEDRIVERENUM +_DEVICE.fields_by_name['device_endpoints'].message_type = _ENDPOINT +_DEVICECONFIG.fields_by_name['config_rules'].message_type = _CONFIGRULE +_DEVICEIDLIST.fields_by_name['device_ids'].message_type = _DEVICEID +_DEVICELIST.fields_by_name['devices'].message_type = _DEVICE +_DEVICEEVENT.fields_by_name['event'].message_type = _EVENT +_DEVICEEVENT.fields_by_name['device_id'].message_type = _DEVICEID +_LINKID.fields_by_name['link_uuid'].message_type = _UUID +_LINK.fields_by_name['link_id'].message_type = _LINKID +_LINK.fields_by_name['link_endpoint_ids'].message_type = _ENDPOINTID +_LINKIDLIST.fields_by_name['link_ids'].message_type = _LINKID +_LINKLIST.fields_by_name['links'].message_type = _LINK +_LINKEVENT.fields_by_name['event'].message_type = _EVENT +_LINKEVENT.fields_by_name['link_id'].message_type = _LINKID +_SERVICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SERVICEID.fields_by_name['service_uuid'].message_type = _UUID +_SERVICE.fields_by_name['service_id'].message_type = _SERVICEID +_SERVICE.fields_by_name['service_type'].enum_type = _SERVICETYPEENUM +_SERVICE.fields_by_name['service_endpoint_ids'].message_type = _ENDPOINTID +_SERVICE.fields_by_name['service_constraints'].message_type = _CONSTRAINT +_SERVICE.fields_by_name['service_status'].message_type = _SERVICESTATUS +_SERVICE.fields_by_name['service_config'].message_type = _SERVICECONFIG +_SERVICESTATUS.fields_by_name['service_status'].enum_type = _SERVICESTATUSENUM +_SERVICECONFIG.fields_by_name['config_rules'].message_type = _CONFIGRULE +_SERVICEIDLIST.fields_by_name['service_ids'].message_type = _SERVICEID +_SERVICELIST.fields_by_name['services'].message_type = _SERVICE +_SERVICEEVENT.fields_by_name['event'].message_type = _EVENT +_SERVICEEVENT.fields_by_name['service_id'].message_type = _SERVICEID +_SLICEID.fields_by_name['context_id'].message_type = _CONTEXTID +_SLICEID.fields_by_name['slice_uuid'].message_type = _UUID +_SLICE.fields_by_name['slice_id'].message_type = _SLICEID +_SLICE.fields_by_name['slice_endpoint_ids'].message_type = _ENDPOINTID +_SLICE.fields_by_name['slice_constraints'].message_type = _CONSTRAINT +_SLICE.fields_by_name['slice_service_ids'].message_type = _SERVICEID +_SLICE.fields_by_name['slice_subslice_ids'].message_type = _SLICEID +_SLICE.fields_by_name['slice_status'].message_type = _SLICESTATUS +_SLICESTATUS.fields_by_name['slice_status'].enum_type = _SLICESTATUSENUM +_SLICEIDLIST.fields_by_name['slice_ids'].message_type = _SLICEID +_SLICELIST.fields_by_name['slices'].message_type = _SLICE +_SLICEEVENT.fields_by_name['event'].message_type = _EVENT +_SLICEEVENT.fields_by_name['slice_id'].message_type = _SLICEID +_CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID +_CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID +_CONNECTION.fields_by_name['service_id'].message_type = _SERVICEID +_CONNECTION.fields_by_name['path_hops_endpoint_ids'].message_type = _ENDPOINTID +_CONNECTION.fields_by_name['sub_service_ids'].message_type = _SERVICEID +_CONNECTIONIDLIST.fields_by_name['connection_ids'].message_type = _CONNECTIONID +_CONNECTIONLIST.fields_by_name['connections'].message_type = _CONNECTION +_CONNECTIONEVENT.fields_by_name['event'].message_type = _EVENT +_CONNECTIONEVENT.fields_by_name['connection_id'].message_type = _CONNECTIONID +_ENDPOINTID.fields_by_name['topology_id'].message_type = _TOPOLOGYID +_ENDPOINTID.fields_by_name['device_id'].message_type = _DEVICEID +_ENDPOINTID.fields_by_name['endpoint_uuid'].message_type = _UUID +_ENDPOINT.fields_by_name['endpoint_id'].message_type = _ENDPOINTID +_ENDPOINT.fields_by_name['kpi_sample_types'].enum_type = kpi__sample__types__pb2._KPISAMPLETYPE +_CONFIGRULE.fields_by_name['action'].enum_type = _CONFIGACTIONENUM +_TERAFLOWCONTROLLER.fields_by_name['context_id'].message_type = _CONTEXTID +_AUTHENTICATIONRESULT.fields_by_name['context_id'].message_type = _CONTEXTID +DESCRIPTOR.message_types_by_name['Empty'] = _EMPTY +DESCRIPTOR.message_types_by_name['Uuid'] = _UUID +DESCRIPTOR.message_types_by_name['Event'] = _EVENT +DESCRIPTOR.message_types_by_name['ContextId'] = _CONTEXTID +DESCRIPTOR.message_types_by_name['Context'] = _CONTEXT +DESCRIPTOR.message_types_by_name['ContextIdList'] = _CONTEXTIDLIST +DESCRIPTOR.message_types_by_name['ContextList'] = _CONTEXTLIST +DESCRIPTOR.message_types_by_name['ContextEvent'] = _CONTEXTEVENT +DESCRIPTOR.message_types_by_name['TopologyId'] = _TOPOLOGYID +DESCRIPTOR.message_types_by_name['Topology'] = _TOPOLOGY +DESCRIPTOR.message_types_by_name['TopologyIdList'] = _TOPOLOGYIDLIST +DESCRIPTOR.message_types_by_name['TopologyList'] = _TOPOLOGYLIST +DESCRIPTOR.message_types_by_name['TopologyEvent'] = _TOPOLOGYEVENT +DESCRIPTOR.message_types_by_name['DeviceId'] = _DEVICEID +DESCRIPTOR.message_types_by_name['Device'] = _DEVICE +DESCRIPTOR.message_types_by_name['DeviceConfig'] = _DEVICECONFIG +DESCRIPTOR.message_types_by_name['DeviceIdList'] = _DEVICEIDLIST +DESCRIPTOR.message_types_by_name['DeviceList'] = _DEVICELIST +DESCRIPTOR.message_types_by_name['DeviceEvent'] = _DEVICEEVENT +DESCRIPTOR.message_types_by_name['LinkId'] = _LINKID +DESCRIPTOR.message_types_by_name['Link'] = _LINK +DESCRIPTOR.message_types_by_name['LinkIdList'] = _LINKIDLIST +DESCRIPTOR.message_types_by_name['LinkList'] = _LINKLIST +DESCRIPTOR.message_types_by_name['LinkEvent'] = _LINKEVENT +DESCRIPTOR.message_types_by_name['ServiceId'] = _SERVICEID +DESCRIPTOR.message_types_by_name['Service'] = _SERVICE +DESCRIPTOR.message_types_by_name['ServiceStatus'] = _SERVICESTATUS +DESCRIPTOR.message_types_by_name['ServiceConfig'] = _SERVICECONFIG +DESCRIPTOR.message_types_by_name['ServiceIdList'] = _SERVICEIDLIST +DESCRIPTOR.message_types_by_name['ServiceList'] = _SERVICELIST +DESCRIPTOR.message_types_by_name['ServiceEvent'] = _SERVICEEVENT +DESCRIPTOR.message_types_by_name['SliceId'] = _SLICEID +DESCRIPTOR.message_types_by_name['Slice'] = _SLICE +DESCRIPTOR.message_types_by_name['SliceStatus'] = _SLICESTATUS +DESCRIPTOR.message_types_by_name['SliceIdList'] = _SLICEIDLIST +DESCRIPTOR.message_types_by_name['SliceList'] = _SLICELIST +DESCRIPTOR.message_types_by_name['SliceEvent'] = _SLICEEVENT +DESCRIPTOR.message_types_by_name['ConnectionId'] = _CONNECTIONID +DESCRIPTOR.message_types_by_name['Connection'] = _CONNECTION +DESCRIPTOR.message_types_by_name['ConnectionIdList'] = _CONNECTIONIDLIST +DESCRIPTOR.message_types_by_name['ConnectionList'] = _CONNECTIONLIST +DESCRIPTOR.message_types_by_name['ConnectionEvent'] = _CONNECTIONEVENT +DESCRIPTOR.message_types_by_name['EndPointId'] = _ENDPOINTID +DESCRIPTOR.message_types_by_name['EndPoint'] = _ENDPOINT +DESCRIPTOR.message_types_by_name['ConfigRule'] = _CONFIGRULE +DESCRIPTOR.message_types_by_name['Constraint'] = _CONSTRAINT +DESCRIPTOR.message_types_by_name['TeraFlowController'] = _TERAFLOWCONTROLLER +DESCRIPTOR.message_types_by_name['AuthenticationResult'] = _AUTHENTICATIONRESULT +DESCRIPTOR.enum_types_by_name['EventTypeEnum'] = _EVENTTYPEENUM +DESCRIPTOR.enum_types_by_name['DeviceDriverEnum'] = _DEVICEDRIVERENUM +DESCRIPTOR.enum_types_by_name['DeviceOperationalStatusEnum'] = _DEVICEOPERATIONALSTATUSENUM +DESCRIPTOR.enum_types_by_name['ServiceTypeEnum'] = _SERVICETYPEENUM +DESCRIPTOR.enum_types_by_name['ServiceStatusEnum'] = _SERVICESTATUSENUM +DESCRIPTOR.enum_types_by_name['SliceStatusEnum'] = _SLICESTATUSENUM +DESCRIPTOR.enum_types_by_name['ConfigActionEnum'] = _CONFIGACTIONENUM +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), { + 'DESCRIPTOR' : _EMPTY, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Empty) + }) +_sym_db.RegisterMessage(Empty) + +Uuid = _reflection.GeneratedProtocolMessageType('Uuid', (_message.Message,), { + 'DESCRIPTOR' : _UUID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Uuid) + }) +_sym_db.RegisterMessage(Uuid) + +Event = _reflection.GeneratedProtocolMessageType('Event', (_message.Message,), { + 'DESCRIPTOR' : _EVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Event) + }) +_sym_db.RegisterMessage(Event) + +ContextId = _reflection.GeneratedProtocolMessageType('ContextId', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextId) + }) +_sym_db.RegisterMessage(ContextId) + +Context = _reflection.GeneratedProtocolMessageType('Context', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Context) + }) +_sym_db.RegisterMessage(Context) + +ContextIdList = _reflection.GeneratedProtocolMessageType('ContextIdList', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextIdList) + }) +_sym_db.RegisterMessage(ContextIdList) + +ContextList = _reflection.GeneratedProtocolMessageType('ContextList', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextList) + }) +_sym_db.RegisterMessage(ContextList) + +ContextEvent = _reflection.GeneratedProtocolMessageType('ContextEvent', (_message.Message,), { + 'DESCRIPTOR' : _CONTEXTEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ContextEvent) + }) +_sym_db.RegisterMessage(ContextEvent) + +TopologyId = _reflection.GeneratedProtocolMessageType('TopologyId', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyId) + }) +_sym_db.RegisterMessage(TopologyId) + +Topology = _reflection.GeneratedProtocolMessageType('Topology', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGY, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Topology) + }) +_sym_db.RegisterMessage(Topology) + +TopologyIdList = _reflection.GeneratedProtocolMessageType('TopologyIdList', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyIdList) + }) +_sym_db.RegisterMessage(TopologyIdList) + +TopologyList = _reflection.GeneratedProtocolMessageType('TopologyList', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyList) + }) +_sym_db.RegisterMessage(TopologyList) + +TopologyEvent = _reflection.GeneratedProtocolMessageType('TopologyEvent', (_message.Message,), { + 'DESCRIPTOR' : _TOPOLOGYEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TopologyEvent) + }) +_sym_db.RegisterMessage(TopologyEvent) + +DeviceId = _reflection.GeneratedProtocolMessageType('DeviceId', (_message.Message,), { + 'DESCRIPTOR' : _DEVICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceId) + }) +_sym_db.RegisterMessage(DeviceId) + +Device = _reflection.GeneratedProtocolMessageType('Device', (_message.Message,), { + 'DESCRIPTOR' : _DEVICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Device) + }) +_sym_db.RegisterMessage(Device) + +DeviceConfig = _reflection.GeneratedProtocolMessageType('DeviceConfig', (_message.Message,), { + 'DESCRIPTOR' : _DEVICECONFIG, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceConfig) + }) +_sym_db.RegisterMessage(DeviceConfig) + +DeviceIdList = _reflection.GeneratedProtocolMessageType('DeviceIdList', (_message.Message,), { + 'DESCRIPTOR' : _DEVICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceIdList) + }) +_sym_db.RegisterMessage(DeviceIdList) + +DeviceList = _reflection.GeneratedProtocolMessageType('DeviceList', (_message.Message,), { + 'DESCRIPTOR' : _DEVICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceList) + }) +_sym_db.RegisterMessage(DeviceList) + +DeviceEvent = _reflection.GeneratedProtocolMessageType('DeviceEvent', (_message.Message,), { + 'DESCRIPTOR' : _DEVICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.DeviceEvent) + }) +_sym_db.RegisterMessage(DeviceEvent) + +LinkId = _reflection.GeneratedProtocolMessageType('LinkId', (_message.Message,), { + 'DESCRIPTOR' : _LINKID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkId) + }) +_sym_db.RegisterMessage(LinkId) + +Link = _reflection.GeneratedProtocolMessageType('Link', (_message.Message,), { + 'DESCRIPTOR' : _LINK, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Link) + }) +_sym_db.RegisterMessage(Link) + +LinkIdList = _reflection.GeneratedProtocolMessageType('LinkIdList', (_message.Message,), { + 'DESCRIPTOR' : _LINKIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkIdList) + }) +_sym_db.RegisterMessage(LinkIdList) + +LinkList = _reflection.GeneratedProtocolMessageType('LinkList', (_message.Message,), { + 'DESCRIPTOR' : _LINKLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkList) + }) +_sym_db.RegisterMessage(LinkList) + +LinkEvent = _reflection.GeneratedProtocolMessageType('LinkEvent', (_message.Message,), { + 'DESCRIPTOR' : _LINKEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.LinkEvent) + }) +_sym_db.RegisterMessage(LinkEvent) + +ServiceId = _reflection.GeneratedProtocolMessageType('ServiceId', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceId) + }) +_sym_db.RegisterMessage(ServiceId) + +Service = _reflection.GeneratedProtocolMessageType('Service', (_message.Message,), { + 'DESCRIPTOR' : _SERVICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Service) + }) +_sym_db.RegisterMessage(Service) + +ServiceStatus = _reflection.GeneratedProtocolMessageType('ServiceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SERVICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceStatus) + }) +_sym_db.RegisterMessage(ServiceStatus) + +ServiceConfig = _reflection.GeneratedProtocolMessageType('ServiceConfig', (_message.Message,), { + 'DESCRIPTOR' : _SERVICECONFIG, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceConfig) + }) +_sym_db.RegisterMessage(ServiceConfig) + +ServiceIdList = _reflection.GeneratedProtocolMessageType('ServiceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceIdList) + }) +_sym_db.RegisterMessage(ServiceIdList) + +ServiceList = _reflection.GeneratedProtocolMessageType('ServiceList', (_message.Message,), { + 'DESCRIPTOR' : _SERVICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceList) + }) +_sym_db.RegisterMessage(ServiceList) + +ServiceEvent = _reflection.GeneratedProtocolMessageType('ServiceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SERVICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ServiceEvent) + }) +_sym_db.RegisterMessage(ServiceEvent) + +SliceId = _reflection.GeneratedProtocolMessageType('SliceId', (_message.Message,), { + 'DESCRIPTOR' : _SLICEID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceId) + }) +_sym_db.RegisterMessage(SliceId) + +Slice = _reflection.GeneratedProtocolMessageType('Slice', (_message.Message,), { + 'DESCRIPTOR' : _SLICE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Slice) + }) +_sym_db.RegisterMessage(Slice) + +SliceStatus = _reflection.GeneratedProtocolMessageType('SliceStatus', (_message.Message,), { + 'DESCRIPTOR' : _SLICESTATUS, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceStatus) + }) +_sym_db.RegisterMessage(SliceStatus) + +SliceIdList = _reflection.GeneratedProtocolMessageType('SliceIdList', (_message.Message,), { + 'DESCRIPTOR' : _SLICEIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceIdList) + }) +_sym_db.RegisterMessage(SliceIdList) + +SliceList = _reflection.GeneratedProtocolMessageType('SliceList', (_message.Message,), { + 'DESCRIPTOR' : _SLICELIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceList) + }) +_sym_db.RegisterMessage(SliceList) + +SliceEvent = _reflection.GeneratedProtocolMessageType('SliceEvent', (_message.Message,), { + 'DESCRIPTOR' : _SLICEEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.SliceEvent) + }) +_sym_db.RegisterMessage(SliceEvent) + +ConnectionId = _reflection.GeneratedProtocolMessageType('ConnectionId', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionId) + }) +_sym_db.RegisterMessage(ConnectionId) + +Connection = _reflection.GeneratedProtocolMessageType('Connection', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTION, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Connection) + }) +_sym_db.RegisterMessage(Connection) + +ConnectionIdList = _reflection.GeneratedProtocolMessageType('ConnectionIdList', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONIDLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionIdList) + }) +_sym_db.RegisterMessage(ConnectionIdList) + +ConnectionList = _reflection.GeneratedProtocolMessageType('ConnectionList', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONLIST, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionList) + }) +_sym_db.RegisterMessage(ConnectionList) + +ConnectionEvent = _reflection.GeneratedProtocolMessageType('ConnectionEvent', (_message.Message,), { + 'DESCRIPTOR' : _CONNECTIONEVENT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConnectionEvent) + }) +_sym_db.RegisterMessage(ConnectionEvent) + +EndPointId = _reflection.GeneratedProtocolMessageType('EndPointId', (_message.Message,), { + 'DESCRIPTOR' : _ENDPOINTID, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.EndPointId) + }) +_sym_db.RegisterMessage(EndPointId) + +EndPoint = _reflection.GeneratedProtocolMessageType('EndPoint', (_message.Message,), { + 'DESCRIPTOR' : _ENDPOINT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.EndPoint) + }) +_sym_db.RegisterMessage(EndPoint) + +ConfigRule = _reflection.GeneratedProtocolMessageType('ConfigRule', (_message.Message,), { + 'DESCRIPTOR' : _CONFIGRULE, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.ConfigRule) + }) +_sym_db.RegisterMessage(ConfigRule) + +Constraint = _reflection.GeneratedProtocolMessageType('Constraint', (_message.Message,), { + 'DESCRIPTOR' : _CONSTRAINT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.Constraint) + }) +_sym_db.RegisterMessage(Constraint) + +TeraFlowController = _reflection.GeneratedProtocolMessageType('TeraFlowController', (_message.Message,), { + 'DESCRIPTOR' : _TERAFLOWCONTROLLER, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.TeraFlowController) + }) +_sym_db.RegisterMessage(TeraFlowController) + +AuthenticationResult = _reflection.GeneratedProtocolMessageType('AuthenticationResult', (_message.Message,), { + 'DESCRIPTOR' : _AUTHENTICATIONRESULT, + '__module__' : 'context_pb2' + # @@protoc_insertion_point(class_scope:context.AuthenticationResult) + }) +_sym_db.RegisterMessage(AuthenticationResult) + + + +_CONTEXTSERVICE = _descriptor.ServiceDescriptor( + name='ContextService', + full_name='context.ContextService', + file=DESCRIPTOR, + index=0, + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_start=5273, + serialized_end=7688, + methods=[ + _descriptor.MethodDescriptor( + name='ListContextIds', + full_name='context.ContextService.ListContextIds', + index=0, + containing_service=None, + input_type=_EMPTY, + output_type=_CONTEXTIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListContexts', + full_name='context.ContextService.ListContexts', + index=1, + containing_service=None, + input_type=_EMPTY, + output_type=_CONTEXTLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetContext', + full_name='context.ContextService.GetContext', + index=2, + containing_service=None, + input_type=_CONTEXTID, + output_type=_CONTEXT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetContext', + full_name='context.ContextService.SetContext', + index=3, + containing_service=None, + input_type=_CONTEXT, + output_type=_CONTEXTID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveContext', + full_name='context.ContextService.RemoveContext', + index=4, + containing_service=None, + input_type=_CONTEXTID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetContextEvents', + full_name='context.ContextService.GetContextEvents', + index=5, + containing_service=None, + input_type=_EMPTY, + output_type=_CONTEXTEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListTopologyIds', + full_name='context.ContextService.ListTopologyIds', + index=6, + containing_service=None, + input_type=_CONTEXTID, + output_type=_TOPOLOGYIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListTopologies', + full_name='context.ContextService.ListTopologies', + index=7, + containing_service=None, + input_type=_CONTEXTID, + output_type=_TOPOLOGYLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetTopology', + full_name='context.ContextService.GetTopology', + index=8, + containing_service=None, + input_type=_TOPOLOGYID, + output_type=_TOPOLOGY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetTopology', + full_name='context.ContextService.SetTopology', + index=9, + containing_service=None, + input_type=_TOPOLOGY, + output_type=_TOPOLOGYID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveTopology', + full_name='context.ContextService.RemoveTopology', + index=10, + containing_service=None, + input_type=_TOPOLOGYID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetTopologyEvents', + full_name='context.ContextService.GetTopologyEvents', + index=11, + containing_service=None, + input_type=_EMPTY, + output_type=_TOPOLOGYEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListDeviceIds', + full_name='context.ContextService.ListDeviceIds', + index=12, + containing_service=None, + input_type=_EMPTY, + output_type=_DEVICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListDevices', + full_name='context.ContextService.ListDevices', + index=13, + containing_service=None, + input_type=_EMPTY, + output_type=_DEVICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetDevice', + full_name='context.ContextService.GetDevice', + index=14, + containing_service=None, + input_type=_DEVICEID, + output_type=_DEVICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetDevice', + full_name='context.ContextService.SetDevice', + index=15, + containing_service=None, + input_type=_DEVICE, + output_type=_DEVICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveDevice', + full_name='context.ContextService.RemoveDevice', + index=16, + containing_service=None, + input_type=_DEVICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetDeviceEvents', + full_name='context.ContextService.GetDeviceEvents', + index=17, + containing_service=None, + input_type=_EMPTY, + output_type=_DEVICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListLinkIds', + full_name='context.ContextService.ListLinkIds', + index=18, + containing_service=None, + input_type=_EMPTY, + output_type=_LINKIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListLinks', + full_name='context.ContextService.ListLinks', + index=19, + containing_service=None, + input_type=_EMPTY, + output_type=_LINKLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetLink', + full_name='context.ContextService.GetLink', + index=20, + containing_service=None, + input_type=_LINKID, + output_type=_LINK, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetLink', + full_name='context.ContextService.SetLink', + index=21, + containing_service=None, + input_type=_LINK, + output_type=_LINKID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveLink', + full_name='context.ContextService.RemoveLink', + index=22, + containing_service=None, + input_type=_LINKID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetLinkEvents', + full_name='context.ContextService.GetLinkEvents', + index=23, + containing_service=None, + input_type=_EMPTY, + output_type=_LINKEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListServiceIds', + full_name='context.ContextService.ListServiceIds', + index=24, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SERVICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListServices', + full_name='context.ContextService.ListServices', + index=25, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SERVICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetService', + full_name='context.ContextService.GetService', + index=26, + containing_service=None, + input_type=_SERVICEID, + output_type=_SERVICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetService', + full_name='context.ContextService.SetService', + index=27, + containing_service=None, + input_type=_SERVICE, + output_type=_SERVICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveService', + full_name='context.ContextService.RemoveService', + index=28, + containing_service=None, + input_type=_SERVICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetServiceEvents', + full_name='context.ContextService.GetServiceEvents', + index=29, + containing_service=None, + input_type=_EMPTY, + output_type=_SERVICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSliceIds', + full_name='context.ContextService.ListSliceIds', + index=30, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICEIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListSlices', + full_name='context.ContextService.ListSlices', + index=31, + containing_service=None, + input_type=_CONTEXTID, + output_type=_SLICELIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSlice', + full_name='context.ContextService.GetSlice', + index=32, + containing_service=None, + input_type=_SLICEID, + output_type=_SLICE, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetSlice', + full_name='context.ContextService.SetSlice', + index=33, + containing_service=None, + input_type=_SLICE, + output_type=_SLICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveSlice', + full_name='context.ContextService.RemoveSlice', + index=34, + containing_service=None, + input_type=_SLICEID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetSliceEvents', + full_name='context.ContextService.GetSliceEvents', + index=35, + containing_service=None, + input_type=_EMPTY, + output_type=_SLICEEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListConnectionIds', + full_name='context.ContextService.ListConnectionIds', + index=36, + containing_service=None, + input_type=_SERVICEID, + output_type=_CONNECTIONIDLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ListConnections', + full_name='context.ContextService.ListConnections', + index=37, + containing_service=None, + input_type=_SERVICEID, + output_type=_CONNECTIONLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetConnection', + full_name='context.ContextService.GetConnection', + index=38, + containing_service=None, + input_type=_CONNECTIONID, + output_type=_CONNECTION, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='SetConnection', + full_name='context.ContextService.SetConnection', + index=39, + containing_service=None, + input_type=_CONNECTION, + output_type=_CONNECTIONID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='RemoveConnection', + full_name='context.ContextService.RemoveConnection', + index=40, + containing_service=None, + input_type=_CONNECTIONID, + output_type=_EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetConnectionEvents', + full_name='context.ContextService.GetConnectionEvents', + index=41, + containing_service=None, + input_type=_EMPTY, + output_type=_CONNECTIONEVENT, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), +]) +_sym_db.RegisterServiceDescriptor(_CONTEXTSERVICE) + +DESCRIPTOR.services_by_name['ContextService'] = _CONTEXTSERVICE + +# @@protoc_insertion_point(module_scope) diff --git a/src/l3_centralizedattackdetector/proto/l3_attackmitigator_pb2_grpc.py b/src/l3_centralizedattackdetector/proto/l3_attackmitigator_pb2_grpc.py deleted file mode 100644 index 25d4afdba..000000000 --- a/src/l3_centralizedattackdetector/proto/l3_attackmitigator_pb2_grpc.py +++ /dev/null @@ -1,102 +0,0 @@ -# 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 l3_attackmitigator_pb2 as l3__attackmitigator__pb2 - - -class L3AttackmitigatorStub(object): - """Missing associated documentation comment in .proto file.""" - - def __init__(self, channel): - """Constructor. - - Args: - channel: A grpc.Channel. - """ - self.SendOutput = channel.unary_unary( - '/L3Attackmitigator/SendOutput', - request_serializer=l3__attackmitigator__pb2.L3AttackmitigatorOutput.SerializeToString, - response_deserializer=context__pb2.Empty.FromString, - ) - self.GetMitigation = channel.unary_unary( - '/L3Attackmitigator/GetMitigation', - request_serializer=context__pb2.Empty.SerializeToString, - response_deserializer=context__pb2.Empty.FromString, - ) - - -class L3AttackmitigatorServicer(object): - """Missing associated documentation comment in .proto file.""" - - def SendOutput(self, request, context): - """Sends a greeting - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - def GetMitigation(self, request, context): - """Sends another greeting - """ - context.set_code(grpc.StatusCode.UNIMPLEMENTED) - context.set_details('Method not implemented!') - raise NotImplementedError('Method not implemented!') - - -def add_L3AttackmitigatorServicer_to_server(servicer, server): - rpc_method_handlers = { - 'SendOutput': grpc.unary_unary_rpc_method_handler( - servicer.SendOutput, - request_deserializer=l3__attackmitigator__pb2.L3AttackmitigatorOutput.FromString, - response_serializer=context__pb2.Empty.SerializeToString, - ), - 'GetMitigation': grpc.unary_unary_rpc_method_handler( - servicer.GetMitigation, - request_deserializer=context__pb2.Empty.FromString, - response_serializer=context__pb2.Empty.SerializeToString, - ), - } - generic_handler = grpc.method_handlers_generic_handler( - 'L3Attackmitigator', rpc_method_handlers) - server.add_generic_rpc_handlers((generic_handler,)) - - - # This class is part of an EXPERIMENTAL API. -class L3Attackmitigator(object): - """Missing associated documentation comment in .proto file.""" - - @staticmethod - def SendOutput(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, '/L3Attackmitigator/SendOutput', - l3__attackmitigator__pb2.L3AttackmitigatorOutput.SerializeToString, - context__pb2.Empty.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) - - @staticmethod - def GetMitigation(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, '/L3Attackmitigator/GetMitigation', - context__pb2.Empty.SerializeToString, - context__pb2.Empty.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) -- GitLab From 8cb8c01bc1b03bf26f3bef5ecd7f93fa5ac74b90 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 12:59:12 +0200 Subject: [PATCH 033/353] Build of component l3_distributedattackdetector fails, deactivating from pipeline. --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 35f5f12ee..0560144f6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,7 +35,8 @@ include: - local: '/src/opticalcentralizedattackdetector/.gitlab-ci.yml' - local: '/src/automation/.gitlab-ci.yml' - local: '/src/webui/.gitlab-ci.yml' - - local: '/src/l3_distributedattackdetector/.gitlab-ci.yml' + #- local: '/src/l3_distributedattackdetector/.gitlab-ci.yml' - local: '/src/l3_centralizedattackdetector/.gitlab-ci.yml' - local: '/src/l3_attackmitigator/.gitlab-ci.yml' #- local: '/src/slice/.gitlab-ci.yml' + #- local: '/src/interdomain/.gitlab-ci.yml' -- GitLab From 236c7418bd93cdabdf2ed2aa9d668c8d664fddc0 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 13:17:29 +0200 Subject: [PATCH 034/353] Corrected genproto script for l3_centralizedattackdetector. --- src/l3_centralizedattackdetector/genproto.sh | 3 + .../proto/kpi_sample_types_pb2.py | 78 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 src/l3_centralizedattackdetector/proto/kpi_sample_types_pb2.py diff --git a/src/l3_centralizedattackdetector/genproto.sh b/src/l3_centralizedattackdetector/genproto.sh index 217994f32..54b16a486 100755 --- a/src/l3_centralizedattackdetector/genproto.sh +++ b/src/l3_centralizedattackdetector/genproto.sh @@ -37,13 +37,16 @@ tee proto/__init__.py << EOF > /dev/null EOF 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 kpi_sample_types.proto python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto l3_attackmitigator.proto python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto l3_centralizedattackdetector.proto rm proto/context_pb2_grpc.py +rm proto/kpi_sample_types_pb2_grpc.py rm proto/l3_attackmitigator_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/kpi_sample_types_pb2.py sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/l3_centralizedattackdetector_pb2.py sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/l3_centralizedattackdetector_pb2_grpc.py sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/l3_attackmitigator_pb2.py diff --git a/src/l3_centralizedattackdetector/proto/kpi_sample_types_pb2.py b/src/l3_centralizedattackdetector/proto/kpi_sample_types_pb2.py new file mode 100644 index 000000000..ea7fd2f82 --- /dev/null +++ b/src/l3_centralizedattackdetector/proto/kpi_sample_types_pb2.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: kpi_sample_types.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() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='kpi_sample_types.proto', + package='kpi_sample_types', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x16kpi_sample_types.proto\x12\x10kpi_sample_types*\xbe\x01\n\rKpiSampleType\x12\x19\n\x15KPISAMPLETYPE_UNKNOWN\x10\x00\x12%\n!KPISAMPLETYPE_PACKETS_TRANSMITTED\x10\x65\x12\"\n\x1eKPISAMPLETYPE_PACKETS_RECEIVED\x10\x66\x12$\n\x1fKPISAMPLETYPE_BYTES_TRANSMITTED\x10\xc9\x01\x12!\n\x1cKPISAMPLETYPE_BYTES_RECEIVED\x10\xca\x01\x62\x06proto3' +) + +_KPISAMPLETYPE = _descriptor.EnumDescriptor( + name='KpiSampleType', + full_name='kpi_sample_types.KpiSampleType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_PACKETS_TRANSMITTED', index=1, number=101, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_PACKETS_RECEIVED', index=2, number=102, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_BYTES_TRANSMITTED', index=3, number=201, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_BYTES_RECEIVED', index=4, number=202, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=45, + serialized_end=235, +) +_sym_db.RegisterEnumDescriptor(_KPISAMPLETYPE) + +KpiSampleType = enum_type_wrapper.EnumTypeWrapper(_KPISAMPLETYPE) +KPISAMPLETYPE_UNKNOWN = 0 +KPISAMPLETYPE_PACKETS_TRANSMITTED = 101 +KPISAMPLETYPE_PACKETS_RECEIVED = 102 +KPISAMPLETYPE_BYTES_TRANSMITTED = 201 +KPISAMPLETYPE_BYTES_RECEIVED = 202 + + +DESCRIPTOR.enum_types_by_name['KpiSampleType'] = _KPISAMPLETYPE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + +# @@protoc_insertion_point(module_scope) -- GitLab From f0a29edfd9e2e4fec70c6367e2ad9c8a2c0af148 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 13:17:36 +0200 Subject: [PATCH 035/353] Corrected genproto script for l3_attackmitigator. --- src/l3_attackmitigator/genproto.sh | 3 + .../proto/kpi_sample_types_pb2.py | 78 +++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 src/l3_attackmitigator/proto/kpi_sample_types_pb2.py diff --git a/src/l3_attackmitigator/genproto.sh b/src/l3_attackmitigator/genproto.sh index 8b6092c8d..c69f7d025 100755 --- a/src/l3_attackmitigator/genproto.sh +++ b/src/l3_attackmitigator/genproto.sh @@ -37,10 +37,13 @@ tee proto/__init__.py << EOF > /dev/null EOF 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 kpi_sample_types.proto python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto l3_attackmitigator.proto rm proto/context_pb2_grpc.py +rm proto/kpi_sample_types_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/kpi_sample_types_pb2.py sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/l3_attackmitigator_pb2.py sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/l3_attackmitigator_pb2_grpc.py diff --git a/src/l3_attackmitigator/proto/kpi_sample_types_pb2.py b/src/l3_attackmitigator/proto/kpi_sample_types_pb2.py new file mode 100644 index 000000000..ea7fd2f82 --- /dev/null +++ b/src/l3_attackmitigator/proto/kpi_sample_types_pb2.py @@ -0,0 +1,78 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: kpi_sample_types.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() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='kpi_sample_types.proto', + package='kpi_sample_types', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x16kpi_sample_types.proto\x12\x10kpi_sample_types*\xbe\x01\n\rKpiSampleType\x12\x19\n\x15KPISAMPLETYPE_UNKNOWN\x10\x00\x12%\n!KPISAMPLETYPE_PACKETS_TRANSMITTED\x10\x65\x12\"\n\x1eKPISAMPLETYPE_PACKETS_RECEIVED\x10\x66\x12$\n\x1fKPISAMPLETYPE_BYTES_TRANSMITTED\x10\xc9\x01\x12!\n\x1cKPISAMPLETYPE_BYTES_RECEIVED\x10\xca\x01\x62\x06proto3' +) + +_KPISAMPLETYPE = _descriptor.EnumDescriptor( + name='KpiSampleType', + full_name='kpi_sample_types.KpiSampleType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_PACKETS_TRANSMITTED', index=1, number=101, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_PACKETS_RECEIVED', index=2, number=102, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_BYTES_TRANSMITTED', index=3, number=201, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='KPISAMPLETYPE_BYTES_RECEIVED', index=4, number=202, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=45, + serialized_end=235, +) +_sym_db.RegisterEnumDescriptor(_KPISAMPLETYPE) + +KpiSampleType = enum_type_wrapper.EnumTypeWrapper(_KPISAMPLETYPE) +KPISAMPLETYPE_UNKNOWN = 0 +KPISAMPLETYPE_PACKETS_TRANSMITTED = 101 +KPISAMPLETYPE_PACKETS_RECEIVED = 102 +KPISAMPLETYPE_BYTES_TRANSMITTED = 201 +KPISAMPLETYPE_BYTES_RECEIVED = 202 + + +DESCRIPTOR.enum_types_by_name['KpiSampleType'] = _KPISAMPLETYPE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + +# @@protoc_insertion_point(module_scope) -- GitLab From f8af80b60af2c2249f57f11eaac3d5db06c51b59 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Date: Tue, 3 May 2022 13:22:15 +0200 Subject: [PATCH 036/353] Unitary tests of l3_* components fail. Deactivated from pipeline to proceed with merges. --- .gitlab-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0560144f6..668b5ce62 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -36,7 +36,7 @@ include: - local: '/src/automation/.gitlab-ci.yml' - local: '/src/webui/.gitlab-ci.yml' #- local: '/src/l3_distributedattackdetector/.gitlab-ci.yml' - - local: '/src/l3_centralizedattackdetector/.gitlab-ci.yml' - - local: '/src/l3_attackmitigator/.gitlab-ci.yml' + #- local: '/src/l3_centralizedattackdetector/.gitlab-ci.yml' + #- local: '/src/l3_attackmitigator/.gitlab-ci.yml' #- local: '/src/slice/.gitlab-ci.yml' #- local: '/src/interdomain/.gitlab-ci.yml' -- GitLab From d8fbf1f9f405a08a4134cf04dada941b93ba2219 Mon Sep 17 00:00:00 2001 From: Carlos Natalino Date: Mon, 5 Sep 2022 10:31:08 +0200 Subject: [PATCH 037/353] Including XML report files to the list of files ignored by git. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 71b77da25..86434f04b 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,7 @@ coverage.xml .pytest_cache/ .benchmarks/ cover/ +*_report.xml # Translations *.mo -- GitLab From dc934c3d5da59353158af3313cfe3e415d5d7bc1 Mon Sep 17 00:00:00 2001 From: cmanso Date: Tue, 6 Sep 2022 15:10:16 +0200 Subject: [PATCH 038/353] Context model updated to SQLAlchemy --- src/common/Constants.py | 4 +- src/context/requirements.in | 3 + src/context/service/Database.py | 25 ++++ src/context/service/__main__.py | 26 +++- src/context/service/database/Base.py | 2 + src/context/service/database/ContextModel.py | 24 ++-- .../service/grpc_server/ContextService.py | 9 +- .../grpc_server/ContextServiceServicerImpl.py | 121 ++++++++++-------- src/context/tests/test_unitary.py | 110 ++++++++++------ 9 files changed, 214 insertions(+), 110 deletions(-) create mode 100644 src/context/service/Database.py create mode 100644 src/context/service/database/Base.py diff --git a/src/common/Constants.py b/src/common/Constants.py index f18d43840..03f34a410 100644 --- a/src/common/Constants.py +++ b/src/common/Constants.py @@ -30,8 +30,8 @@ DEFAULT_HTTP_BIND_ADDRESS = '0.0.0.0' DEFAULT_METRICS_PORT = 9192 # Default context and topology UUIDs -DEFAULT_CONTEXT_UUID = 'admin' -DEFAULT_TOPOLOGY_UUID = 'admin' +DEFAULT_CONTEXT_UUID = '85f78267-4c5e-4f80-ad2f-7fbaca7c62a0' +DEFAULT_TOPOLOGY_UUID = '85f78267-4c5e-4f80-ad2f-7fbaca7c62a0' # Default service names class ServiceNameEnum(Enum): diff --git a/src/context/requirements.in b/src/context/requirements.in index 9cc7e71f2..6e07456fc 100644 --- a/src/context/requirements.in +++ b/src/context/requirements.in @@ -2,3 +2,6 @@ Flask==2.1.3 Flask-RESTful==0.3.9 redis==4.1.2 requests==2.27.1 +sqlalchemy==1.4.40 +sqlalchemy-cockroachdb +psycopg2-binary diff --git a/src/context/service/Database.py b/src/context/service/Database.py new file mode 100644 index 000000000..e25e2319c --- /dev/null +++ b/src/context/service/Database.py @@ -0,0 +1,25 @@ +from sqlalchemy.orm import Session +from context.service.database.Base import Base +import logging + +LOGGER = logging.getLogger(__name__) + + +class Database(Session): + def __init__(self, session): + super().__init__() + self.session = session + + def query_all(self, model): + result = [] + with self.session() as session: + for entry in session.query(model).all(): + result.append(entry) + + return result + + def clear(self): + with self.session() as session: + engine = session.get_bind() + Base.metadata.drop_all(engine) + Base.metadata.create_all(engine) diff --git a/src/context/service/__main__.py b/src/context/service/__main__.py index 53754caf4..154c8ff00 100644 --- a/src/context/service/__main__.py +++ b/src/context/service/__main__.py @@ -15,15 +15,18 @@ import logging, signal, sys, threading from prometheus_client import start_http_server from common.Settings import get_log_level, get_metrics_port, get_setting -from common.orm.Database import Database -from common.orm.Factory import get_database_backend from common.message_broker.Factory import get_messagebroker_backend from common.message_broker.MessageBroker import MessageBroker from context.Config import POPULATE_FAKE_DATA +from sqlalchemy.orm import sessionmaker, declarative_base +from context.service.database.Base import Base from .grpc_server.ContextService import ContextService from .rest_server.Resources import RESOURCES from .rest_server.RestServer import RestServer from .Populate import populate +# from models import Device, EndPoint, EndPointId, DeviceDriverEnum, DeviceOperationalStatusEnum, ConfigActionEnum, \ +# ConfigRule, KpiSampleType, Base +from sqlalchemy import create_engine terminate = threading.Event() LOGGER = None @@ -49,18 +52,31 @@ def main(): start_http_server(metrics_port) # Get database instance - database = Database(get_database_backend()) + db_uri = 'cockroachdb://root@10.152.183.121:26257/defaultdb?sslmode=disable' + LOGGER.debug('Connecting to DB: {}'.format(db_uri)) + + # engine = create_engine(db_uri, echo=False) + + try: + engine = create_engine(db_uri) + except Exception as e: + LOGGER.error("Failed to connect to database.") + LOGGER.error(f"{e}") + return 1 + + Base.metadata.create_all(engine) + session = sessionmaker(bind=engine) # Get message broker instance messagebroker = MessageBroker(get_messagebroker_backend()) # Starting context service - grpc_service = ContextService(database, messagebroker) + grpc_service = ContextService(session, messagebroker) grpc_service.start() rest_server = RestServer() for endpoint_name, resource_class, resource_url in RESOURCES: - rest_server.add_resource(resource_class, resource_url, endpoint=endpoint_name, resource_class_args=(database,)) + rest_server.add_resource(resource_class, resource_url, endpoint=endpoint_name, resource_class_args=(session,)) rest_server.start() populate_fake_data = get_setting('POPULATE_FAKE_DATA', default=POPULATE_FAKE_DATA) diff --git a/src/context/service/database/Base.py b/src/context/service/database/Base.py new file mode 100644 index 000000000..c64447da1 --- /dev/null +++ b/src/context/service/database/Base.py @@ -0,0 +1,2 @@ +from sqlalchemy.ext.declarative import declarative_base +Base = declarative_base() diff --git a/src/context/service/database/ContextModel.py b/src/context/service/database/ContextModel.py index a12e6669d..ba55fd566 100644 --- a/src/context/service/database/ContextModel.py +++ b/src/context/service/database/ContextModel.py @@ -14,19 +14,23 @@ import logging from typing import Dict, List -from common.orm.fields.PrimaryKeyField import PrimaryKeyField -from common.orm.fields.StringField import StringField -from common.orm.model.Model import Model +from sqlalchemy import Column +from sqlalchemy.dialects.postgresql import UUID +from context.service.database.Base import Base + LOGGER = logging.getLogger(__name__) -class ContextModel(Model): - pk = PrimaryKeyField() - context_uuid = StringField(required=True, allow_empty=False) + +class ContextModel(Base): + __tablename__ = 'Context' + + context_uuid = Column(UUID(as_uuid=False), primary_key=True) def dump_id(self) -> Dict: return {'context_uuid': {'uuid': self.context_uuid}} + """ def dump_service_ids(self) -> List[Dict]: from .ServiceModel import ServiceModel # pylint: disable=import-outside-toplevel db_service_pks = self.references(ServiceModel) @@ -36,9 +40,11 @@ class ContextModel(Model): from .TopologyModel import TopologyModel # pylint: disable=import-outside-toplevel db_topology_pks = self.references(TopologyModel) return [TopologyModel(self.database, pk).dump_id() for pk,_ in db_topology_pks] + """ - def dump(self, include_services=True, include_topologies=True) -> Dict: # pylint: disable=arguments-differ + def dump(self, include_services=True, include_topologies=True) -> Dict: # pylint: disable=arguments-differ result = {'context_id': self.dump_id()} - if include_services: result['service_ids'] = self.dump_service_ids() - if include_topologies: result['topology_ids'] = self.dump_topology_ids() + # if include_services: result['service_ids'] = self.dump_service_ids() + # if include_topologies: result['topology_ids'] = self.dump_topology_ids() return result + diff --git a/src/context/service/grpc_server/ContextService.py b/src/context/service/grpc_server/ContextService.py index 1b54ec540..d029b54e0 100644 --- a/src/context/service/grpc_server/ContextService.py +++ b/src/context/service/grpc_server/ContextService.py @@ -15,19 +15,22 @@ from common.Constants import ServiceNameEnum from common.Settings import get_service_port_grpc from common.message_broker.MessageBroker import MessageBroker -from common.orm.Database import Database from common.proto.context_pb2_grpc import add_ContextServiceServicer_to_server from common.tools.service.GenericGrpcService import GenericGrpcService +from sqlalchemy.orm import Session +import logging + from .ContextServiceServicerImpl import ContextServiceServicerImpl # Custom gRPC settings GRPC_MAX_WORKERS = 200 # multiple clients might keep connections alive for Get*Events() RPC methods +LOGGER = logging.getLogger(__name__) class ContextService(GenericGrpcService): - def __init__(self, database : Database, messagebroker : MessageBroker, cls_name: str = __name__) -> None: + def __init__(self, session : Session, messagebroker : MessageBroker, cls_name: str = __name__) -> None: port = get_service_port_grpc(ServiceNameEnum.CONTEXT) super().__init__(port, max_workers=GRPC_MAX_WORKERS, cls_name=cls_name) - self.context_servicer = ContextServiceServicerImpl(database, messagebroker) + self.context_servicer = ContextServiceServicerImpl(session, messagebroker) def install_servicers(self): add_ContextServiceServicer_to_server(self.context_servicer, self.server) diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py index 4c8f957ec..36f79a15c 100644 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py @@ -31,10 +31,13 @@ from common.proto.context_pb2 import ( from common.proto.context_pb2_grpc import ContextServiceServicer from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException +from sqlalchemy.orm import Session +from common.rpc_method_wrapper.ServiceExceptions import NotFoundException + +""" from context.service.database.ConfigModel import grpc_config_rules_to_raw, update_config from context.service.database.ConnectionModel import ConnectionModel, set_path from context.service.database.ConstraintModel import set_constraints -from context.service.database.ContextModel import ContextModel from context.service.database.DeviceModel import DeviceModel, grpc_to_enum__device_operational_status, set_drivers from context.service.database.EndPointModel import EndPointModel, set_kpi_sample_types from context.service.database.Events import notify_event @@ -46,6 +49,11 @@ from context.service.database.ServiceModel import ( ServiceModel, grpc_to_enum__service_status, grpc_to_enum__service_type) from context.service.database.SliceModel import SliceModel, grpc_to_enum__slice_status from context.service.database.TopologyModel import TopologyModel +""" +from context.service.database.ContextModel import ContextModel +# from context.service.database.TopologyModel import TopologyModel +from context.service.database.Events import notify_event + from .Constants import ( CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE, TOPIC_TOPOLOGY) @@ -65,10 +73,10 @@ METHOD_NAMES = [ METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) class ContextServiceServicerImpl(ContextServiceServicer): - def __init__(self, database : Database, messagebroker : MessageBroker): + def __init__(self, session : Session, messagebroker : MessageBroker): LOGGER.debug('Creating Servicer...') self.lock = threading.Lock() - self.database = database + self.session = session self.messagebroker = messagebroker LOGGER.debug('Servicer Created') @@ -77,77 +85,83 @@ class ContextServiceServicerImpl(ContextServiceServicer): @safe_and_metered_rpc_method(METRICS, LOGGER) def ListContextIds(self, request: Empty, context : grpc.ServicerContext) -> ContextIdList: - with self.lock: - db_contexts : List[ContextModel] = get_all_objects(self.database, ContextModel) - db_contexts = sorted(db_contexts, key=operator.attrgetter('pk')) - return ContextIdList(context_ids=[db_context.dump_id() for db_context in db_contexts]) + with self.session() as session: + result = session.query(ContextModel).all() + + return ContextIdList(context_ids=[row.dump_id() for row in result]) + @safe_and_metered_rpc_method(METRICS, LOGGER) def ListContexts(self, request: Empty, context : grpc.ServicerContext) -> ContextList: - with self.lock: - db_contexts : List[ContextModel] = get_all_objects(self.database, ContextModel) - db_contexts = sorted(db_contexts, key=operator.attrgetter('pk')) - return ContextList(contexts=[db_context.dump() for db_context in db_contexts]) + with self.session() as session: + result = session.query(ContextModel).all() + + return ContextList(contexts=[row.dump() for row in result]) + @safe_and_metered_rpc_method(METRICS, LOGGER) def GetContext(self, request: ContextId, context : grpc.ServicerContext) -> Context: - with self.lock: - context_uuid = request.context_uuid.uuid - db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) - return Context(**db_context.dump(include_services=True, include_topologies=True)) + context_uuid = request.context_uuid.uuid + with self.session() as session: + result = session.query(ContextModel).filter_by(context_uuid=context_uuid).one_or_none() + + if not result: + raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) + + return Context(**result.dump()) @safe_and_metered_rpc_method(METRICS, LOGGER) def SetContext(self, request: Context, context : grpc.ServicerContext) -> ContextId: - with self.lock: - context_uuid = request.context_id.context_uuid.uuid + context_uuid = request.context_id.context_uuid.uuid - for i,topology_id in enumerate(request.topology_ids): - topology_context_uuid = topology_id.context_id.context_uuid.uuid - if topology_context_uuid != context_uuid: - raise InvalidArgumentException( - 'request.topology_ids[{:d}].context_id.context_uuid.uuid'.format(i), topology_context_uuid, - ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)]) + for i, topology_id in enumerate(request.topology_ids): + topology_context_uuid = topology_id.context_id.context_uuid.uuid + if topology_context_uuid != context_uuid: + raise InvalidArgumentException( + 'request.topology_ids[{:d}].context_id.context_uuid.uuid'.format(i), topology_context_uuid, + ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)]) - for i,service_id in enumerate(request.service_ids): - service_context_uuid = service_id.context_id.context_uuid.uuid - if service_context_uuid != context_uuid: - raise InvalidArgumentException( - 'request.service_ids[{:d}].context_id.context_uuid.uuid'.format(i), service_context_uuid, - ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)]) + for i, service_id in enumerate(request.service_ids): + service_context_uuid = service_id.context_id.context_uuid.uuid + if service_context_uuid != context_uuid: + raise InvalidArgumentException( + 'request.service_ids[{:d}].context_id.context_uuid.uuid'.format(i), service_context_uuid, + ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)]) - result : Tuple[ContextModel, bool] = update_or_create_object( - self.database, ContextModel, context_uuid, {'context_uuid': context_uuid}) - db_context, updated = result + context_add = ContextModel(context_uuid=context_uuid) - for i,topology_id in enumerate(request.topology_ids): - topology_context_uuid = topology_id.context_id.context_uuid.uuid - topology_uuid = topology_id.topology_uuid.uuid - get_object(self.database, TopologyModel, [context_uuid, topology_uuid]) # just to confirm it exists + updated = True + with self.session() as session: + result = session.query(ContextModel).filter_by(context_uuid=context_uuid).all() + if not result: + updated = False - for i,service_id in enumerate(request.service_ids): - service_context_uuid = service_id.context_id.context_uuid.uuid - service_uuid = service_id.service_uuid.uuid - get_object(self.database, ServiceModel, [context_uuid, service_uuid]) # just to confirm it exists + with self.session() as session: + session.merge(context_add) + session.commit() + + + event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE + dict_context_id = context_add.dump_id() + notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': dict_context_id}) + return ContextId(**context_add.dump_id()) - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_context_id = db_context.dump_id() - notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': dict_context_id}) - return ContextId(**dict_context_id) @safe_and_metered_rpc_method(METRICS, LOGGER) def RemoveContext(self, request: ContextId, context : grpc.ServicerContext) -> Empty: - with self.lock: - context_uuid = request.context_uuid.uuid - db_context = ContextModel(self.database, context_uuid, auto_load=False) - found = db_context.load() - if not found: return Empty() - - dict_context_id = db_context.dump_id() - db_context.delete() + context_uuid = request.context_uuid.uuid + + with self.session() as session: + result = session.query(ContextModel).filter_by(context_uuid=context_uuid).one_or_none() + if not result: + return Empty() + session.query(ContextModel).filter_by(context_uuid=context_uuid).delete() + session.commit() event_type = EventTypeEnum.EVENTTYPE_REMOVE - notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': dict_context_id}) + notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': result.dump_id()}) return Empty() + """ @safe_and_metered_rpc_method(METRICS, LOGGER) def GetContextEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ContextEvent]: for message in self.messagebroker.consume({TOPIC_CONTEXT}, consume_timeout=CONSUME_TIMEOUT): @@ -761,3 +775,4 @@ class ContextServiceServicerImpl(ContextServiceServicer): def GetConnectionEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ConnectionEvent]: for message in self.messagebroker.consume({TOPIC_CONNECTION}, consume_timeout=CONSUME_TIMEOUT): yield ConnectionEvent(**json.loads(message.content)) + """ \ No newline at end of file diff --git a/src/context/tests/test_unitary.py b/src/context/tests/test_unitary.py index b46c9468c..0879dcb06 100644 --- a/src/context/tests/test_unitary.py +++ b/src/context/tests/test_unitary.py @@ -19,7 +19,7 @@ from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID, Servic from common.Settings import ( ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, ENVVAR_SUFIX_SERVICE_PORT_HTTP, get_env_var_name, get_service_baseurl_http, get_service_port_grpc, get_service_port_http) -from common.orm.Database import Database +from context.service.Database import Database from common.orm.Factory import get_database_backend, BackendEnum as DatabaseBackendEnum from common.message_broker.Factory import get_messagebroker_backend, BackendEnum as MessageBrokerBackendEnum from common.message_broker.MessageBroker import MessageBroker @@ -40,6 +40,12 @@ from context.service.grpc_server.ContextService import ContextService from context.service.Populate import populate from context.service.rest_server.RestServer import RestServer from context.service.rest_server.Resources import RESOURCES +from requests import Session +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from context.service.database.ContextModel import ContextModel +from context.service.database.Base import Base + from .Objects import ( CONNECTION_R1_R3, CONNECTION_R1_R3_ID, CONNECTION_R1_R3_UUID, CONTEXT, CONTEXT_ID, DEVICE_R1, DEVICE_R1_ID, DEVICE_R1_UUID, DEVICE_R2, DEVICE_R2_ID, DEVICE_R2_UUID, DEVICE_R3, DEVICE_R3_ID, DEVICE_R3_UUID, LINK_R1_R2, @@ -50,8 +56,8 @@ LOGGER = logging.getLogger(__name__) LOGGER.setLevel(logging.DEBUG) LOCAL_HOST = '127.0.0.1' -GRPC_PORT = 10000 + get_service_port_grpc(ServiceNameEnum.CONTEXT) # avoid privileged ports -HTTP_PORT = 10000 + get_service_port_http(ServiceNameEnum.CONTEXT) # avoid privileged ports +GRPC_PORT = 10000 + int(get_service_port_grpc(ServiceNameEnum.CONTEXT)) # avoid privileged ports +HTTP_PORT = 10000 + int(get_service_port_http(ServiceNameEnum.CONTEXT)) # avoid privileged ports os.environ[get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_HOST )] = str(LOCAL_HOST) os.environ[get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(GRPC_PORT) @@ -68,12 +74,10 @@ REDIS_CONFIG = { } SCENARIOS = [ - ('all_inmemory', DatabaseBackendEnum.INMEMORY, {}, MessageBrokerBackendEnum.INMEMORY, {} ), - ('all_redis', DatabaseBackendEnum.REDIS, REDIS_CONFIG, MessageBrokerBackendEnum.REDIS, REDIS_CONFIG), + ('all_sqlalchemy', {}, MessageBrokerBackendEnum.INMEMORY, {} ), ] - @pytest.fixture(scope='session', ids=[str(scenario[0]) for scenario in SCENARIOS], params=SCENARIOS) -def context_db_mb(request) -> Tuple[Database, MessageBroker]: +def context_db_mb(request) -> Tuple[Session, MessageBroker]: name,db_backend,db_settings,mb_backend,mb_settings = request.param msg = 'Running scenario {:s} db_backend={:s}, db_settings={:s}, mb_backend={:s}, mb_settings={:s}...' LOGGER.info(msg.format(str(name), str(db_backend.value), str(db_settings), str(mb_backend.value), str(mb_settings))) @@ -82,13 +86,36 @@ def context_db_mb(request) -> Tuple[Database, MessageBroker]: yield _database, _message_broker _message_broker.terminate() +@pytest.fixture(scope='session', ids=[str(scenario[0]) for scenario in SCENARIOS], params=SCENARIOS) +def context_s_mb(request) -> Tuple[Session, MessageBroker]: + name,db_session,mb_backend,mb_settings = request.param + msg = 'Running scenario {:s} db_session={:s}, mb_backend={:s}, mb_settings={:s}...' + LOGGER.info(msg.format(str(name), str(db_session), str(mb_backend.value), str(mb_settings))) + + db_uri = 'cockroachdb://root@10.152.183.121:26257/defaultdb?sslmode=disable' + LOGGER.debug('Connecting to DB: {}'.format(db_uri)) + + try: + engine = create_engine(db_uri) + except Exception as e: + LOGGER.error("Failed to connect to database.") + LOGGER.error(f"{e}") + return 1 + + Base.metadata.create_all(engine) + _session = sessionmaker(bind=engine) + + _message_broker = MessageBroker(get_messagebroker_backend(backend=mb_backend, **mb_settings)) + yield _session, _message_broker + _message_broker.terminate() + @pytest.fixture(scope='session') -def context_service_grpc(context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name - _service = ContextService(context_db_mb[0], context_db_mb[1]) +def context_service_grpc(context_s_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name + _service = ContextService(context_s_mb[0], context_s_mb[1]) _service.start() yield _service _service.stop() - +""" @pytest.fixture(scope='session') def context_service_rest(context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name database = context_db_mb[0] @@ -100,13 +127,13 @@ def context_service_rest(context_db_mb : Tuple[Database, MessageBroker]): # pyli yield _rest_server _rest_server.shutdown() _rest_server.join() - +""" @pytest.fixture(scope='session') def context_client_grpc(context_service_grpc : ContextService): # pylint: disable=redefined-outer-name _client = ContextClient() yield _client _client.close() - +""" def do_rest_request(url : str): base_url = get_service_baseurl_http(ServiceNameEnum.CONTEXT) request_url = 'http://{:s}:{:s}{:s}{:s}'.format(str(LOCAL_HOST), str(HTTP_PORT), str(base_url), url) @@ -115,18 +142,18 @@ def do_rest_request(url : str): LOGGER.warning('Reply: {:s}'.format(str(reply.text))) assert reply.status_code == 200, 'Reply failed with code {}'.format(reply.status_code) return reply.json() - +""" # ----- Test gRPC methods ---------------------------------------------------------------------------------------------- - def test_grpc_context( context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name - context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name - context_database = context_db_mb[0] + context_s_mb : Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name + Session = context_s_mb[0] - # ----- Clean the database ----------------------------------------------------------------------------------------- - context_database.clear_all() + database = Database(Session) + # ----- Clean the database ----------------------------------------------------------------------------------------- + database.clear() # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- events_collector = EventsCollector(context_client_grpc) events_collector.start() @@ -145,7 +172,7 @@ def test_grpc_context( assert len(response.contexts) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ - db_entries = context_database.dump() + db_entries = database.query_all(ContextModel) LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) for db_entry in db_entries: LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover @@ -156,51 +183,56 @@ def test_grpc_context( response = context_client_grpc.SetContext(Context(**CONTEXT)) assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + wrong_uuid = 'c97c4185-e1d1-4ea7-b6b9-afbf76cb61f4' with pytest.raises(grpc.RpcError) as e: WRONG_TOPOLOGY_ID = copy.deepcopy(TOPOLOGY_ID) - WRONG_TOPOLOGY_ID['context_id']['context_uuid']['uuid'] = 'wrong-context-uuid' + WRONG_TOPOLOGY_ID['context_id']['context_uuid']['uuid'] = wrong_uuid WRONG_CONTEXT = copy.deepcopy(CONTEXT) WRONG_CONTEXT['topology_ids'].append(WRONG_TOPOLOGY_ID) context_client_grpc.SetContext(Context(**WRONG_CONTEXT)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'request.topology_ids[0].context_id.context_uuid.uuid(wrong-context-uuid) is invalid; '\ - 'should be == request.context_id.context_uuid.uuid(admin)' + msg = 'request.topology_ids[0].context_id.context_uuid.uuid({}) is invalid; '\ + 'should be == request.context_id.context_uuid.uuid({})'.format(wrong_uuid, DEFAULT_CONTEXT_UUID) assert e.value.details() == msg with pytest.raises(grpc.RpcError) as e: WRONG_SERVICE_ID = copy.deepcopy(SERVICE_R1_R2_ID) - WRONG_SERVICE_ID['context_id']['context_uuid']['uuid'] = 'wrong-context-uuid' + WRONG_SERVICE_ID['context_id']['context_uuid']['uuid'] = wrong_uuid WRONG_CONTEXT = copy.deepcopy(CONTEXT) WRONG_CONTEXT['service_ids'].append(WRONG_SERVICE_ID) context_client_grpc.SetContext(Context(**WRONG_CONTEXT)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'request.service_ids[0].context_id.context_uuid.uuid(wrong-context-uuid) is invalid; '\ - 'should be == request.context_id.context_uuid.uuid(admin)' + msg = 'request.service_ids[0].context_id.context_uuid.uuid({}) is invalid; '\ + 'should be == request.context_id.context_uuid.uuid({})'.format(wrong_uuid, DEFAULT_CONTEXT_UUID) assert e.value.details() == msg # ----- Check create event ----------------------------------------------------------------------------------------- + """ event = events_collector.get_event(block=True) assert isinstance(event, ContextEvent) assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - + """ # ----- Update the object ------------------------------------------------------------------------------------------ response = context_client_grpc.SetContext(Context(**CONTEXT)) assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Check update event ----------------------------------------------------------------------------------------- + """ event = events_collector.get_event(block=True) assert isinstance(event, ContextEvent) assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + """ # ----- Dump state of database after create/update the object ------------------------------------------------------ - db_entries = context_database.dump() + db_entries = database.query_all(ContextModel) + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + # for db_entry in db_entries: + # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 2 + assert len(db_entries) == 1 # ----- Get when the object exists --------------------------------------------------------------------------------- response = context_client_grpc.GetContext(ContextId(**CONTEXT_ID)) @@ -223,22 +255,23 @@ def test_grpc_context( context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) # ----- Check remove event ----------------------------------------------------------------------------------------- - event = events_collector.get_event(block=True) - assert isinstance(event, ContextEvent) - assert event.event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # event = events_collector.get_event(block=True) + # assert isinstance(event, ContextEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- events_collector.stop() # ----- Dump state of database after remove the object ------------------------------------------------------------- - db_entries = context_database.dump() + db_entries = database.query_all(ContextModel) + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + # for db_entry in db_entries: + # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 - + """ def test_grpc_topology( context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name @@ -1293,3 +1326,4 @@ def test_tools_fast_string_hasher(): fast_hasher(('hello', 'world')) fast_hasher(['hello'.encode('UTF-8'), 'world'.encode('UTF-8')]) fast_hasher(('hello'.encode('UTF-8'), 'world'.encode('UTF-8'))) +""" \ No newline at end of file -- GitLab From 1a9c0447ddc647e5c8dea16f9c3ec3577a2c7f81 Mon Sep 17 00:00:00 2001 From: cmanso Date: Tue, 13 Sep 2022 14:58:32 +0200 Subject: [PATCH 039/353] Topology model updated to SQLAlchemy --- src/context/service/Database.py | 3 + src/context/service/database/ContextModel.py | 5 +- src/context/service/database/TopologyModel.py | 26 ++-- .../grpc_server/ContextServiceServicerImpl.py | 130 +++++++++--------- src/context/tests/test_unitary.py | 106 +++++++------- 5 files changed, 136 insertions(+), 134 deletions(-) diff --git a/src/context/service/Database.py b/src/context/service/Database.py index e25e2319c..281761ed8 100644 --- a/src/context/service/Database.py +++ b/src/context/service/Database.py @@ -18,6 +18,9 @@ class Database(Session): return result + def get_object(self): + pass + def clear(self): with self.session() as session: engine = session.get_bind() diff --git a/src/context/service/database/ContextModel.py b/src/context/service/database/ContextModel.py index ba55fd566..77a95ea03 100644 --- a/src/context/service/database/ContextModel.py +++ b/src/context/service/database/ContextModel.py @@ -17,6 +17,7 @@ from typing import Dict, List from sqlalchemy import Column from sqlalchemy.dialects.postgresql import UUID from context.service.database.Base import Base +from sqlalchemy.orm import relationship LOGGER = logging.getLogger(__name__) @@ -24,9 +25,11 @@ LOGGER = logging.getLogger(__name__) class ContextModel(Base): __tablename__ = 'Context' - context_uuid = Column(UUID(as_uuid=False), primary_key=True) + # Relationships + topology = relationship("TopologyModel", back_populates="context") + def dump_id(self) -> Dict: return {'context_uuid': {'uuid': self.context_uuid}} diff --git a/src/context/service/database/TopologyModel.py b/src/context/service/database/TopologyModel.py index 5909c7a2c..9f117c73c 100644 --- a/src/context/service/database/TopologyModel.py +++ b/src/context/service/database/TopologyModel.py @@ -19,23 +19,28 @@ from common.orm.fields.PrimaryKeyField import PrimaryKeyField from common.orm.fields.StringField import StringField from common.orm.model.Model import Model from common.orm.HighLevel import get_related_objects -from .ContextModel import ContextModel - +from sqlalchemy.orm import relationship +from sqlalchemy import Column, ForeignKey +from sqlalchemy.dialects.postgresql import UUID +from context.service.database.Base import Base LOGGER = logging.getLogger(__name__) -class TopologyModel(Model): - pk = PrimaryKeyField() - context_fk = ForeignKeyField(ContextModel) - topology_uuid = StringField(required=True, allow_empty=False) +class TopologyModel(Base): + __tablename__ = 'Topology' + context_fk = Column(UUID(as_uuid=False), ForeignKey("Context.context_uuid"), nullable=False) + topology_uuid = Column(UUID(as_uuid=False), primary_key=True, nullable=False) + + # Relationships + context = relationship("ContextModel", back_populates="topology", lazy="joined") def dump_id(self) -> Dict: - context_id = ContextModel(self.database, self.context_fk).dump_id() + context_id = self.context.dump_id() return { 'context_id': context_id, 'topology_uuid': {'uuid': self.topology_uuid}, } - def dump_device_ids(self) -> List[Dict]: + """def dump_device_ids(self) -> List[Dict]: from .RelationModels import TopologyDeviceModel # pylint: disable=import-outside-toplevel db_devices = get_related_objects(self, TopologyDeviceModel, 'device_fk') return [db_device.dump_id() for db_device in sorted(db_devices, key=operator.attrgetter('pk'))] @@ -44,11 +49,12 @@ class TopologyModel(Model): from .RelationModels import TopologyLinkModel # pylint: disable=import-outside-toplevel db_links = get_related_objects(self, TopologyLinkModel, 'link_fk') return [db_link.dump_id() for db_link in sorted(db_links, key=operator.attrgetter('pk'))] + """ def dump( # pylint: disable=arguments-differ self, include_devices=True, include_links=True ) -> Dict: result = {'topology_id': self.dump_id()} - if include_devices: result['device_ids'] = self.dump_device_ids() - if include_links: result['link_ids'] = self.dump_link_ids() + # if include_devices: result['device_ids'] = self.dump_device_ids() + # if include_links: result['link_ids'] = self.dump_link_ids() return result diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py index 36f79a15c..bf51bf316 100644 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py @@ -15,10 +15,8 @@ import grpc, json, logging, operator, threading from typing import Iterator, List, Set, Tuple from common.message_broker.MessageBroker import MessageBroker -from common.orm.Database import Database -from common.orm.HighLevel import ( - get_all_objects, get_object, get_or_create_object, get_related_objects, update_or_create_object) -from common.orm.backend.Tools import key_to_str +from context.service.Database import Database + from common.proto.context_pb2 import ( Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, Context, ContextEvent, ContextId, ContextIdList, ContextList, @@ -31,9 +29,10 @@ from common.proto.context_pb2 import ( from common.proto.context_pb2_grpc import ContextServiceServicer from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException -from sqlalchemy.orm import Session +from sqlalchemy.orm import Session, contains_eager, selectinload from common.rpc_method_wrapper.ServiceExceptions import NotFoundException + """ from context.service.database.ConfigModel import grpc_config_rules_to_raw, update_config from context.service.database.ConnectionModel import ConnectionModel, set_path @@ -51,6 +50,7 @@ from context.service.database.SliceModel import SliceModel, grpc_to_enum__slice_ from context.service.database.TopologyModel import TopologyModel """ from context.service.database.ContextModel import ContextModel +from context.service.database.TopologyModel import TopologyModel # from context.service.database.TopologyModel import TopologyModel from context.service.database.Events import notify_event @@ -77,6 +77,7 @@ class ContextServiceServicerImpl(ContextServiceServicer): LOGGER.debug('Creating Servicer...') self.lock = threading.Lock() self.session = session + self.database = Database(session) self.messagebroker = messagebroker LOGGER.debug('Servicer Created') @@ -133,10 +134,8 @@ class ContextServiceServicerImpl(ContextServiceServicer): updated = True with self.session() as session: result = session.query(ContextModel).filter_by(context_uuid=context_uuid).all() - if not result: - updated = False - - with self.session() as session: + if not result: + updated = False session.merge(context_add) session.commit() @@ -161,7 +160,6 @@ class ContextServiceServicerImpl(ContextServiceServicer): notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': result.dump_id()}) return Empty() - """ @safe_and_metered_rpc_method(METRICS, LOGGER) def GetContextEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ContextEvent]: for message in self.messagebroker.consume({TOPIC_CONTEXT}, consume_timeout=CONSUME_TIMEOUT): @@ -174,75 +172,78 @@ class ContextServiceServicerImpl(ContextServiceServicer): def ListTopologyIds(self, request: ContextId, context : grpc.ServicerContext) -> TopologyIdList: with self.lock: context_uuid = request.context_uuid.uuid - db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) - db_topologies : Set[TopologyModel] = get_related_objects(db_context, TopologyModel) - db_topologies = sorted(db_topologies, key=operator.attrgetter('pk')) + + with self.session() as session: + result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by(context_uuid=context_uuid).one_or_none() + if not result: + raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) + + db_topologies = result.topology return TopologyIdList(topology_ids=[db_topology.dump_id() for db_topology in db_topologies]) @safe_and_metered_rpc_method(METRICS, LOGGER) def ListTopologies(self, request: ContextId, context : grpc.ServicerContext) -> TopologyList: - with self.lock: - context_uuid = request.context_uuid.uuid - db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) - db_topologies : Set[TopologyModel] = get_related_objects(db_context, TopologyModel) - db_topologies = sorted(db_topologies, key=operator.attrgetter('pk')) - return TopologyList(topologies=[db_topology.dump() for db_topology in db_topologies]) + context_uuid = request.context_uuid.uuid + + with self.session() as session: + result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by( + context_uuid=context_uuid).one_or_none() + if not result: + raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) + + db_topologies = result.topology + return TopologyList(topologies=[db_topology.dump() for db_topology in db_topologies]) @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology: - with self.lock: - str_key = key_to_str([request.context_id.context_uuid.uuid, request.topology_uuid.uuid]) - db_topology : TopologyModel = get_object(self.database, TopologyModel, str_key) - return Topology(**db_topology.dump(include_devices=True, include_links=True)) + def GetTopology(self, request: TopologyId, contextt : grpc.ServicerContext) -> Topology: + context_uuid = request.context_id.context_uuid.uuid + topology_uuid = request.topology_uuid.uuid + + with self.session() as session: + result = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).options(contains_eager(TopologyModel.context)).one_or_none() + + if not result: + raise NotFoundException(TopologyModel.__name__.replace('Model', ''), topology_uuid) + + return Topology(**result.dump()) + @safe_and_metered_rpc_method(METRICS, LOGGER) def SetTopology(self, request: Topology, context : grpc.ServicerContext) -> TopologyId: - with self.lock: - context_uuid = request.topology_id.context_id.context_uuid.uuid - db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) + context_uuid = request.topology_id.context_id.context_uuid.uuid + topology_uuid = request.topology_id.topology_uuid.uuid + with self.session() as session: + db_context: ContextModel = session.query(ContextModel).filter_by(context_uuid=context_uuid).one() - topology_uuid = request.topology_id.topology_uuid.uuid - str_topology_key = key_to_str([context_uuid, topology_uuid]) - result : Tuple[TopologyModel, bool] = update_or_create_object( - self.database, TopologyModel, str_topology_key, { - 'context_fk': db_context, 'topology_uuid': topology_uuid}) - db_topology,updated = result - - for device_id in request.device_ids: - device_uuid = device_id.device_uuid.uuid - db_device = get_object(self.database, DeviceModel, device_uuid) - str_topology_device_key = key_to_str([str_topology_key, device_uuid], separator='--') - result : Tuple[TopologyDeviceModel, bool] = update_or_create_object( - self.database, TopologyDeviceModel, str_topology_device_key, - {'topology_fk': db_topology, 'device_fk': db_device}) - #db_topology_device,topology_device_updated = result - - for link_id in request.link_ids: - link_uuid = link_id.link_uuid.uuid - db_link = get_object(self.database, LinkModel, link_uuid) - - str_topology_link_key = key_to_str([str_topology_key, link_uuid], separator='--') - result : Tuple[TopologyLinkModel, bool] = update_or_create_object( - self.database, TopologyLinkModel, str_topology_link_key, - {'topology_fk': db_topology, 'link_fk': db_link}) - #db_topology_link,topology_link_updated = result + topology_add = TopologyModel(topology_uuid=topology_uuid, context_fk=context_uuid) + topology_add.context = db_context + updated = True + with self.session() as session: + result = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).options(contains_eager(TopologyModel.context)).one_or_none() - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_topology_id = db_topology.dump_id() - notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) - return TopologyId(**dict_topology_id) + if not result: + updated = False + session.merge(topology_add) + session.commit() + + event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE + dict_topology_id = topology_add.dump_id() + notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) + return TopologyId(**dict_topology_id) @safe_and_metered_rpc_method(METRICS, LOGGER) def RemoveTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Empty: - with self.lock: - context_uuid = request.context_id.context_uuid.uuid - topology_uuid = request.topology_uuid.uuid - db_topology = TopologyModel(self.database, key_to_str([context_uuid, topology_uuid]), auto_load=False) - found = db_topology.load() - if not found: return Empty() + context_uuid = request.context_id.context_uuid.uuid + topology_uuid = request.topology_uuid.uuid - dict_topology_id = db_topology.dump_id() - db_topology.delete() + with self.session() as session: + result = session.query(TopologyModel).filter_by(topology_uuid=topology_uuid, context_fk=context_uuid).one_or_none() + if not result: + return Empty() + dict_topology_id = result.dump_id() + + session.query(TopologyModel).filter_by(topology_uuid=topology_uuid, context_fk=context_uuid).delete() + session.commit() event_type = EventTypeEnum.EVENTTYPE_REMOVE notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) return Empty() @@ -251,6 +252,7 @@ class ContextServiceServicerImpl(ContextServiceServicer): def GetTopologyEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[TopologyEvent]: for message in self.messagebroker.consume({TOPIC_TOPOLOGY}, consume_timeout=CONSUME_TIMEOUT): yield TopologyEvent(**json.loads(message.content)) + """ # ----- Device ----------------------------------------------------------------------------------------------------- diff --git a/src/context/tests/test_unitary.py b/src/context/tests/test_unitary.py index 0879dcb06..b7a9cee92 100644 --- a/src/context/tests/test_unitary.py +++ b/src/context/tests/test_unitary.py @@ -44,6 +44,7 @@ from requests import Session from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from context.service.database.ContextModel import ContextModel +from context.service.database.TopologyModel import TopologyModel from context.service.database.Base import Base from .Objects import ( @@ -76,15 +77,6 @@ REDIS_CONFIG = { SCENARIOS = [ ('all_sqlalchemy', {}, MessageBrokerBackendEnum.INMEMORY, {} ), ] -@pytest.fixture(scope='session', ids=[str(scenario[0]) for scenario in SCENARIOS], params=SCENARIOS) -def context_db_mb(request) -> Tuple[Session, MessageBroker]: - name,db_backend,db_settings,mb_backend,mb_settings = request.param - msg = 'Running scenario {:s} db_backend={:s}, db_settings={:s}, mb_backend={:s}, mb_settings={:s}...' - LOGGER.info(msg.format(str(name), str(db_backend.value), str(db_settings), str(mb_backend.value), str(mb_settings))) - _database = Database(get_database_backend(backend=db_backend, **db_settings)) - _message_broker = MessageBroker(get_messagebroker_backend(backend=mb_backend, **mb_settings)) - yield _database, _message_broker - _message_broker.terminate() @pytest.fixture(scope='session', ids=[str(scenario[0]) for scenario in SCENARIOS], params=SCENARIOS) def context_s_mb(request) -> Tuple[Session, MessageBroker]: @@ -207,23 +199,19 @@ def test_grpc_context( assert e.value.details() == msg # ----- Check create event ----------------------------------------------------------------------------------------- - """ event = events_collector.get_event(block=True) assert isinstance(event, ContextEvent) assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - """ # ----- Update the object ------------------------------------------------------------------------------------------ response = context_client_grpc.SetContext(Context(**CONTEXT)) assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Check update event ----------------------------------------------------------------------------------------- - """ event = events_collector.get_event(block=True) assert isinstance(event, ContextEvent) assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - """ # ----- Dump state of database after create/update the object ------------------------------------------------------ db_entries = database.query_all(ContextModel) @@ -271,15 +259,16 @@ def test_grpc_context( # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 - """ def test_grpc_topology( - context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name - context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name - context_database = context_db_mb[0] + context_client_grpc: ContextClient, # pylint: disable=redefined-outer-name + context_s_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name + session = context_s_mb[0] + + database = Database(session) # ----- Clean the database ----------------------------------------------------------------------------------------- - context_database.clear_all() + database.clear() # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- events_collector = EventsCollector(context_client_grpc) @@ -288,32 +277,30 @@ def test_grpc_topology( # ----- Prepare dependencies for the test and capture related events ----------------------------------------------- response = context_client_grpc.SetContext(Context(**CONTEXT)) assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID - - event = events_collector.get_event(block=True) - assert isinstance(event, ContextEvent) - assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE - assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # event = events_collector.get_event(block=True) + # assert isinstance(event, ContextEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Get when the object does not exist ------------------------------------------------------------------------- with pytest.raises(grpc.RpcError) as e: context_client_grpc.GetTopology(TopologyId(**TOPOLOGY_ID)) assert e.value.code() == grpc.StatusCode.NOT_FOUND - assert e.value.details() == 'Topology({:s}/{:s}) not found'.format(DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID) - + # assert e.value.details() == 'Topology({:s}/{:s}) not found'.format(DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID) + assert e.value.details() == 'Topology({:s}) not found'.format(DEFAULT_TOPOLOGY_UUID) # ----- List when the object does not exist ------------------------------------------------------------------------ response = context_client_grpc.ListTopologyIds(ContextId(**CONTEXT_ID)) assert len(response.topology_ids) == 0 - response = context_client_grpc.ListTopologies(ContextId(**CONTEXT_ID)) assert len(response.topologies) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ - db_entries = context_database.dump() + db_entries = database.query_all(TopologyModel) LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + # for db_entry in db_entries: + # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 2 + assert len(db_entries) == 0 # ----- Create the object ------------------------------------------------------------------------------------------ response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) @@ -326,16 +313,16 @@ def test_grpc_topology( assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Check create event ----------------------------------------------------------------------------------------- - events = events_collector.get_events(block=True, count=2) + # events = events_collector.get_events(block=True, count=2) - assert isinstance(events[0], TopologyEvent) - assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE - assert events[0].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert events[0].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + # assert isinstance(events[0], TopologyEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[0].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert events[0].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID - assert isinstance(events[1], ContextEvent) - assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_UPDATE - assert events[1].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert isinstance(events[1], ContextEvent) + # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + # assert events[1].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Update the object ------------------------------------------------------------------------------------------ response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) @@ -343,19 +330,19 @@ def test_grpc_topology( assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID # ----- Check update event ----------------------------------------------------------------------------------------- - event = events_collector.get_event(block=True) - assert isinstance(event, TopologyEvent) - assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE - assert event.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert event.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + # event = events_collector.get_event(block=True) + # assert isinstance(event, TopologyEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + # assert event.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert event.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID # ----- Dump state of database after create/update the object ------------------------------------------------------ - db_entries = context_database.dump() + db_entries = database.query_all(TopologyModel) LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + # for db_entry in db_entries: + # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 5 + assert len(db_entries) == 1 # ----- Get when the object exists --------------------------------------------------------------------------------- response = context_client_grpc.GetTopology(TopologyId(**TOPOLOGY_ID)) @@ -382,28 +369,29 @@ def test_grpc_topology( context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) # ----- Check remove event ----------------------------------------------------------------------------------------- - events = events_collector.get_events(block=True, count=2) + # events = events_collector.get_events(block=True, count=2) - assert isinstance(events[0], TopologyEvent) - assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - assert events[0].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert events[0].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + # assert isinstance(events[0], TopologyEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[0].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert events[0].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID - assert isinstance(events[1], ContextEvent) - assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - assert events[1].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert isinstance(events[1], ContextEvent) + # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[1].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- - events_collector.stop() + # events_collector.stop() # ----- Dump state of database after remove the object ------------------------------------------------------------- - db_entries = context_database.dump() + db_entries = database.query_all(TopologyModel) LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + # for db_entry in db_entries: + # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 + """ def test_grpc_device( context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name -- GitLab From 979f3d4124a443b0bcbeb8a3b1e3e19030b373eb Mon Sep 17 00:00:00 2001 From: cmanso Date: Tue, 13 Sep 2022 16:24:46 +0200 Subject: [PATCH 040/353] Topology model updated to SQLAlchemy --- .../service/grpc_server/ContextServiceServicerImpl.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py index bf51bf316..9952444b7 100644 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py @@ -195,7 +195,7 @@ class ContextServiceServicerImpl(ContextServiceServicer): return TopologyList(topologies=[db_topology.dump() for db_topology in db_topologies]) @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetTopology(self, request: TopologyId, contextt : grpc.ServicerContext) -> Topology: + def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology: context_uuid = request.context_id.context_uuid.uuid topology_uuid = request.topology_uuid.uuid @@ -215,10 +215,9 @@ class ContextServiceServicerImpl(ContextServiceServicer): with self.session() as session: db_context: ContextModel = session.query(ContextModel).filter_by(context_uuid=context_uuid).one() - topology_add = TopologyModel(topology_uuid=topology_uuid, context_fk=context_uuid) - topology_add.context = db_context - updated = True - with self.session() as session: + topology_add = TopologyModel(topology_uuid=topology_uuid, context_fk=context_uuid) + topology_add.context = db_context + updated = True result = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).options(contains_eager(TopologyModel.context)).one_or_none() if not result: -- GitLab From 0406cd2766ead8b98c7d4c75ae6f06dab0f12697 Mon Sep 17 00:00:00 2001 From: cmanso Date: Tue, 20 Sep 2022 15:02:51 +0200 Subject: [PATCH 041/353] Topology model updated to SQLAlchemy --- src/context/service/__main__.py | 2 +- src/context/service/database/TopologyModel.py | 6 ++-- .../grpc_server/ContextServiceServicerImpl.py | 36 +++++++++---------- src/context/tests/test_unitary.py | 2 +- 4 files changed, 21 insertions(+), 25 deletions(-) diff --git a/src/context/service/__main__.py b/src/context/service/__main__.py index 154c8ff00..937059202 100644 --- a/src/context/service/__main__.py +++ b/src/context/service/__main__.py @@ -52,7 +52,7 @@ def main(): start_http_server(metrics_port) # Get database instance - db_uri = 'cockroachdb://root@10.152.183.121:26257/defaultdb?sslmode=disable' + db_uri = 'cockroachdb://root@10.152.183.66:26257/defaultdb?sslmode=disable' LOGGER.debug('Connecting to DB: {}'.format(db_uri)) # engine = create_engine(db_uri, echo=False) diff --git a/src/context/service/database/TopologyModel.py b/src/context/service/database/TopologyModel.py index 9f117c73c..ec8427b07 100644 --- a/src/context/service/database/TopologyModel.py +++ b/src/context/service/database/TopologyModel.py @@ -27,11 +27,11 @@ LOGGER = logging.getLogger(__name__) class TopologyModel(Base): __tablename__ = 'Topology' - context_fk = Column(UUID(as_uuid=False), ForeignKey("Context.context_uuid"), nullable=False) - topology_uuid = Column(UUID(as_uuid=False), primary_key=True, nullable=False) + context_uuid = Column(UUID(as_uuid=False), ForeignKey("Context.context_uuid"), primary_key=True) + topology_uuid = Column(UUID(as_uuid=False), primary_key=True) # Relationships - context = relationship("ContextModel", back_populates="topology", lazy="joined") + context = relationship("ContextModel", back_populates="topology", lazy="subquery") def dump_id(self) -> Dict: context_id = self.context.dump_id() diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py index 9952444b7..5439b6c06 100644 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py @@ -170,11 +170,10 @@ class ContextServiceServicerImpl(ContextServiceServicer): @safe_and_metered_rpc_method(METRICS, LOGGER) def ListTopologyIds(self, request: ContextId, context : grpc.ServicerContext) -> TopologyIdList: - with self.lock: - context_uuid = request.context_uuid.uuid + context_uuid = request.context_uuid.uuid - with self.session() as session: - result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by(context_uuid=context_uuid).one_or_none() + with self.session() as session: + result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by(context_uuid=context_uuid).one_or_none() if not result: raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) @@ -188,11 +187,11 @@ class ContextServiceServicerImpl(ContextServiceServicer): with self.session() as session: result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by( context_uuid=context_uuid).one_or_none() - if not result: - raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) + if not result: + raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) - db_topologies = result.topology - return TopologyList(topologies=[db_topology.dump() for db_topology in db_topologies]) + db_topologies = result.topology + return TopologyList(topologies=[db_topology.dump() for db_topology in db_topologies]) @safe_and_metered_rpc_method(METRICS, LOGGER) def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology: @@ -213,22 +212,19 @@ class ContextServiceServicerImpl(ContextServiceServicer): context_uuid = request.topology_id.context_id.context_uuid.uuid topology_uuid = request.topology_id.topology_uuid.uuid with self.session() as session: - db_context: ContextModel = session.query(ContextModel).filter_by(context_uuid=context_uuid).one() - - topology_add = TopologyModel(topology_uuid=topology_uuid, context_fk=context_uuid) - topology_add.context = db_context + topology_add = TopologyModel(topology_uuid=topology_uuid, context_uuid=context_uuid) updated = True - result = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).options(contains_eager(TopologyModel.context)).one_or_none() - + result = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).one_or_none() if not result: updated = False session.merge(topology_add) session.commit() + result = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).one_or_none() - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_topology_id = topology_add.dump_id() - notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) - return TopologyId(**dict_topology_id) + event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE + dict_topology_id = result.dump_id() + notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) + return TopologyId(**dict_topology_id) @safe_and_metered_rpc_method(METRICS, LOGGER) def RemoveTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Empty: @@ -236,12 +232,12 @@ class ContextServiceServicerImpl(ContextServiceServicer): topology_uuid = request.topology_uuid.uuid with self.session() as session: - result = session.query(TopologyModel).filter_by(topology_uuid=topology_uuid, context_fk=context_uuid).one_or_none() + result = session.query(TopologyModel).filter_by(topology_uuid=topology_uuid, context_uuid=context_uuid).one_or_none() if not result: return Empty() dict_topology_id = result.dump_id() - session.query(TopologyModel).filter_by(topology_uuid=topology_uuid, context_fk=context_uuid).delete() + session.query(TopologyModel).filter_by(topology_uuid=topology_uuid, context_uuid=context_uuid).delete() session.commit() event_type = EventTypeEnum.EVENTTYPE_REMOVE notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) diff --git a/src/context/tests/test_unitary.py b/src/context/tests/test_unitary.py index b7a9cee92..e202de498 100644 --- a/src/context/tests/test_unitary.py +++ b/src/context/tests/test_unitary.py @@ -84,7 +84,7 @@ def context_s_mb(request) -> Tuple[Session, MessageBroker]: msg = 'Running scenario {:s} db_session={:s}, mb_backend={:s}, mb_settings={:s}...' LOGGER.info(msg.format(str(name), str(db_session), str(mb_backend.value), str(mb_settings))) - db_uri = 'cockroachdb://root@10.152.183.121:26257/defaultdb?sslmode=disable' + db_uri = 'cockroachdb://root@10.152.183.66:26257/defaultdb?sslmode=disable' LOGGER.debug('Connecting to DB: {}'.format(db_uri)) try: -- GitLab From 24301258560fa43cbf981abc472c311b492aa94e Mon Sep 17 00:00:00 2001 From: cmanso Date: Fri, 23 Sep 2022 12:36:30 +0200 Subject: [PATCH 042/353] Topology model updated to SQLAlchemy --- src/context/service/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context/service/__main__.py b/src/context/service/__main__.py index 937059202..93c0e4748 100644 --- a/src/context/service/__main__.py +++ b/src/context/service/__main__.py @@ -52,7 +52,7 @@ def main(): start_http_server(metrics_port) # Get database instance - db_uri = 'cockroachdb://root@10.152.183.66:26257/defaultdb?sslmode=disable' + db_uri = 'cockroachdb://root@10.152.183.111:26257/defaultdb?sslmode=disable' LOGGER.debug('Connecting to DB: {}'.format(db_uri)) # engine = create_engine(db_uri, echo=False) -- GitLab From facab6d65b6413e462284a0c1e49e1fc4cf00bba Mon Sep 17 00:00:00 2001 From: cmanso Date: Mon, 3 Oct 2022 08:54:36 +0200 Subject: [PATCH 043/353] Device model updated to SQLAlchemy --- src/context/service/Database.py | 89 +++++- src/context/service/__main__.py | 2 +- src/context/service/database/ConfigModel.py | 87 ++++-- src/context/service/database/ContextModel.py | 3 + src/context/service/database/DeviceModel.py | 104 ++++--- src/context/service/database/EndPointModel.py | 54 ++-- src/context/service/database/KpiSampleType.py | 4 +- src/context/service/database/Tools.py | 3 +- src/context/service/database/TopologyModel.py | 13 +- .../grpc_server/ContextServiceServicerImpl.py | 280 ++++++++++++------ src/context/tests/Objects.py | 13 +- src/context/tests/test_unitary.py | 119 ++++---- 12 files changed, 507 insertions(+), 264 deletions(-) diff --git a/src/context/service/Database.py b/src/context/service/Database.py index 281761ed8..8fae9f652 100644 --- a/src/context/service/Database.py +++ b/src/context/service/Database.py @@ -1,6 +1,12 @@ +from typing import Tuple, List + +from sqlalchemy import MetaData from sqlalchemy.orm import Session from context.service.database.Base import Base import logging +from common.orm.backend.Tools import key_to_str + +from common.rpc_method_wrapper.ServiceExceptions import NotFoundException LOGGER = logging.getLogger(__name__) @@ -10,7 +16,7 @@ class Database(Session): super().__init__() self.session = session - def query_all(self, model): + def get_all(self, model): result = [] with self.session() as session: for entry in session.query(model).all(): @@ -18,11 +24,88 @@ class Database(Session): return result - def get_object(self): - pass + def create_or_update(self, model): + with self.session() as session: + att = getattr(model, model.main_pk_name()) + filt = {model.main_pk_name(): att} + found = session.query(type(model)).filter_by(**filt).one_or_none() + if found: + found = True + else: + found = False + + session.merge(model) + session.commit() + return model, found + + def create(self, model): + with self.session() as session: + session.add(model) + session.commit() + return model + + def remove(self, model, filter_d): + model_t = type(model) + with self.session() as session: + session.query(model_t).filter_by(**filter_d).delete() + session.commit() + def clear(self): with self.session() as session: engine = session.get_bind() Base.metadata.drop_all(engine) Base.metadata.create_all(engine) + + def dump_by_table(self): + with self.session() as session: + engine = session.get_bind() + meta = MetaData() + meta.reflect(engine) + result = {} + + for table in meta.sorted_tables: + result[table.name] = [dict(row) for row in engine.execute(table.select())] + LOGGER.info(result) + return result + + def dump_all(self): + with self.session() as session: + engine = session.get_bind() + meta = MetaData() + meta.reflect(engine) + result = [] + + for table in meta.sorted_tables: + for row in engine.execute(table.select()): + result.append((table.name, dict(row))) + LOGGER.info(result) + + return result + + def get_object(self, model_class: Base, main_key: str, raise_if_not_found=False): + filt = {model_class.main_pk_name(): main_key} + with self.session() as session: + get = session.query(model_class).filter_by(**filt).one_or_none() + + if not get: + if raise_if_not_found: + raise NotFoundException(model_class.__name__.replace('Model', ''), main_key) + + return get + def get_or_create(self, model_class: Base, key_parts: List[str] + ) -> Tuple[Base, bool]: + + str_key = key_to_str(key_parts) + filt = {model_class.main_pk_name(): key_parts} + with self.session() as session: + get = session.query(model_class).filter_by(**filt).one_or_none() + if get: + return get, False + else: + obj = model_class() + setattr(obj, model_class.main_pk_name(), str_key) + LOGGER.info(obj.dump()) + session.add(obj) + session.commit() + return obj, True diff --git a/src/context/service/__main__.py b/src/context/service/__main__.py index 93c0e4748..9fc2f2357 100644 --- a/src/context/service/__main__.py +++ b/src/context/service/__main__.py @@ -65,7 +65,7 @@ def main(): return 1 Base.metadata.create_all(engine) - session = sessionmaker(bind=engine) + session = sessionmaker(bind=engine, expire_on_commit=False) # Get message broker instance messagebroker = MessageBroker(get_messagebroker_backend()) diff --git a/src/context/service/database/ConfigModel.py b/src/context/service/database/ConfigModel.py index bb2a37467..4dcd50c2c 100644 --- a/src/context/service/database/ConfigModel.py +++ b/src/context/service/database/ConfigModel.py @@ -11,26 +11,23 @@ # 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 enum import functools, logging, operator -from enum import Enum from typing import Dict, List, Optional, Tuple, Union -from common.orm.Database import Database -from common.orm.HighLevel import get_object, get_or_create_object, update_or_create_object from common.orm.backend.Tools import key_to_str -from common.orm.fields.EnumeratedField import EnumeratedField -from common.orm.fields.ForeignKeyField import ForeignKeyField -from common.orm.fields.IntegerField import IntegerField -from common.orm.fields.PrimaryKeyField import PrimaryKeyField -from common.orm.fields.StringField import StringField -from common.orm.model.Model import Model from common.proto.context_pb2 import ConfigActionEnum from common.tools.grpc.Tools import grpc_message_to_json_string +from sqlalchemy import Column, ForeignKey, INTEGER, CheckConstraint, Enum, String +from sqlalchemy.dialects.postgresql import UUID, ARRAY +from context.service.database.Base import Base +from sqlalchemy.orm import relationship +from context.service.Database import Database + from .Tools import fast_hasher, grpc_to_enum, remove_dict_key LOGGER = logging.getLogger(__name__) -class ORM_ConfigActionEnum(Enum): +class ORM_ConfigActionEnum(enum.Enum): UNDEFINED = ConfigActionEnum.CONFIGACTION_UNDEFINED SET = ConfigActionEnum.CONFIGACTION_SET DELETE = ConfigActionEnum.CONFIGACTION_DELETE @@ -38,27 +35,47 @@ class ORM_ConfigActionEnum(Enum): grpc_to_enum__config_action = functools.partial( grpc_to_enum, ConfigActionEnum, ORM_ConfigActionEnum) -class ConfigModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() +class ConfigModel(Base): # pylint: disable=abstract-method + __tablename__ = 'Config' + config_uuid = Column(UUID(as_uuid=False), primary_key=True) + + # Relationships + config_rule = relationship("ConfigRuleModel", back_populates="config", lazy="dynamic") + def delete(self) -> None: db_config_rule_pks = self.references(ConfigRuleModel) for pk,_ in db_config_rule_pks: ConfigRuleModel(self.database, pk).delete() super().delete() - def dump(self) -> List[Dict]: - db_config_rule_pks = self.references(ConfigRuleModel) - config_rules = [ConfigRuleModel(self.database, pk).dump(include_position=True) for pk,_ in db_config_rule_pks] - config_rules = sorted(config_rules, key=operator.itemgetter('position')) + def dump(self): # -> List[Dict]: + config_rules = [] + for a in self.config_rule: + asdf = a.dump() + config_rules.append(asdf) return [remove_dict_key(config_rule, 'position') for config_rule in config_rules] -class ConfigRuleModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - config_fk = ForeignKeyField(ConfigModel) - position = IntegerField(min_value=0, required=True) - action = EnumeratedField(ORM_ConfigActionEnum, required=True) - key = StringField(required=True, allow_empty=False) - value = StringField(required=True, allow_empty=False) + @staticmethod + def main_pk_name(): + return 'config_uuid' + +class ConfigRuleModel(Base): # pylint: disable=abstract-method + __tablename__ = 'ConfigRule' + config_rule_uuid = Column(UUID(as_uuid=False), primary_key=True) + config_uuid = Column(UUID(as_uuid=False), ForeignKey("Config.config_uuid"), primary_key=True) + + action = Column(Enum(ORM_ConfigActionEnum, create_constraint=True, native_enum=True), nullable=False) + position = Column(INTEGER, nullable=False) + key = Column(String, nullable=False) + value = Column(String, nullable=False) + + __table_args__ = ( + CheckConstraint(position >= 0, name='check_position_value'), + {} + ) + + # Relationships + config = relationship("ConfigModel", back_populates="config_rule") def dump(self, include_position=True) -> Dict: # pylint: disable=arguments-differ result = { @@ -71,17 +88,23 @@ class ConfigRuleModel(Model): # pylint: disable=abstract-method if include_position: result['position'] = self.position return result + @staticmethod + def main_pk_name(): + return 'config_rule_uuid' + def set_config_rule( - database : Database, db_config : ConfigModel, position : int, resource_key : str, resource_value : str -) -> Tuple[ConfigRuleModel, bool]: + database : Database, db_config : ConfigModel, position : int, resource_key : str, resource_value : str, +): # -> Tuple[ConfigRuleModel, bool]: str_rule_key_hash = fast_hasher(resource_key) - str_config_rule_key = key_to_str([db_config.pk, str_rule_key_hash], separator=':') - result : Tuple[ConfigRuleModel, bool] = update_or_create_object(database, ConfigRuleModel, str_config_rule_key, { - 'config_fk': db_config, 'position': position, 'action': ORM_ConfigActionEnum.SET, - 'key': resource_key, 'value': resource_value}) - db_config_rule, updated = result - return db_config_rule, updated + str_config_rule_key = key_to_str([db_config.config_uuid, str_rule_key_hash], separator=':') + + data = {'config_fk': db_config, 'position': position, 'action': ORM_ConfigActionEnum.SET, 'key': resource_key, + 'value': resource_value} + to_add = ConfigRuleModel(**data) + + result = database.create_or_update(to_add) + return result def delete_config_rule( database : Database, db_config : ConfigModel, resource_key : str diff --git a/src/context/service/database/ContextModel.py b/src/context/service/database/ContextModel.py index 77a95ea03..ef1d485be 100644 --- a/src/context/service/database/ContextModel.py +++ b/src/context/service/database/ContextModel.py @@ -33,6 +33,9 @@ class ContextModel(Base): def dump_id(self) -> Dict: return {'context_uuid': {'uuid': self.context_uuid}} + def main_pk_name(self): + return 'context_uuid' + """ def dump_service_ids(self) -> List[Dict]: from .ServiceModel import ServiceModel # pylint: disable=import-outside-toplevel diff --git a/src/context/service/database/DeviceModel.py b/src/context/service/database/DeviceModel.py index 0d4232679..bf8f73c79 100644 --- a/src/context/service/database/DeviceModel.py +++ b/src/context/service/database/DeviceModel.py @@ -11,24 +11,22 @@ # 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 enum import functools, logging -from enum import Enum +import uuid from typing import Dict, List from common.orm.Database import Database from common.orm.backend.Tools import key_to_str -from common.orm.fields.EnumeratedField import EnumeratedField -from common.orm.fields.ForeignKeyField import ForeignKeyField -from common.orm.fields.PrimaryKeyField import PrimaryKeyField -from common.orm.fields.StringField import StringField -from common.orm.model.Model import Model from common.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum -from .ConfigModel import ConfigModel +from sqlalchemy import Column, ForeignKey, String, Enum +from sqlalchemy.dialects.postgresql import UUID, ARRAY +from context.service.database.Base import Base +from sqlalchemy.orm import relationship from .Tools import grpc_to_enum LOGGER = logging.getLogger(__name__) -class ORM_DeviceDriverEnum(Enum): +class ORM_DeviceDriverEnum(enum.Enum): UNDEFINED = DeviceDriverEnum.DEVICEDRIVER_UNDEFINED OPENCONFIG = DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG TRANSPORT_API = DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API @@ -39,7 +37,7 @@ class ORM_DeviceDriverEnum(Enum): grpc_to_enum__device_driver = functools.partial( grpc_to_enum, DeviceDriverEnum, ORM_DeviceDriverEnum) -class ORM_DeviceOperationalStatusEnum(Enum): +class ORM_DeviceOperationalStatusEnum(enum.Enum): UNDEFINED = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_UNDEFINED DISABLED = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED ENABLED = DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_ENABLED @@ -47,48 +45,51 @@ class ORM_DeviceOperationalStatusEnum(Enum): grpc_to_enum__device_operational_status = functools.partial( grpc_to_enum, DeviceOperationalStatusEnum, ORM_DeviceOperationalStatusEnum) -class DeviceModel(Model): - pk = PrimaryKeyField() - device_uuid = StringField(required=True, allow_empty=False) - device_type = StringField() - device_config_fk = ForeignKeyField(ConfigModel) - device_operational_status = EnumeratedField(ORM_DeviceOperationalStatusEnum, required=True) - - def delete(self) -> None: - # pylint: disable=import-outside-toplevel - from .EndPointModel import EndPointModel - from .RelationModels import TopologyDeviceModel - - for db_endpoint_pk,_ in self.references(EndPointModel): - EndPointModel(self.database, db_endpoint_pk).delete() - - for db_topology_device_pk,_ in self.references(TopologyDeviceModel): - TopologyDeviceModel(self.database, db_topology_device_pk).delete() - - for db_driver_pk,_ in self.references(DriverModel): - DriverModel(self.database, db_driver_pk).delete() - - super().delete() - - ConfigModel(self.database, self.device_config_fk).delete() +class DeviceModel(Base): + __tablename__ = 'Device' + device_uuid = Column(UUID(as_uuid=False), primary_key=True) + device_type = Column(String) + device_config_uuid = Column(UUID(as_uuid=False), ForeignKey("Config.config_uuid")) + device_operational_status = Column(Enum(ORM_DeviceOperationalStatusEnum, create_constraint=False, + native_enum=False)) + + # Relationships + device_config = relationship("ConfigModel", lazy="joined") + driver = relationship("DriverModel", lazy="joined") + endpoints = relationship("EndPointModel", lazy="joined") + + # def delete(self) -> None: + # # pylint: disable=import-outside-toplevel + # from .EndPointModel import EndPointModel + # from .RelationModels import TopologyDeviceModel + # + # for db_endpoint_pk,_ in self.references(EndPointModel): + # EndPointModel(self.database, db_endpoint_pk).delete() + # + # for db_topology_device_pk,_ in self.references(TopologyDeviceModel): + # TopologyDeviceModel(self.database, db_topology_device_pk).delete() + # + # for db_driver_pk,_ in self.references(DriverModel): + # DriverModel(self.database, db_driver_pk).delete() + # + # super().delete() + # + # ConfigModel(self.database, self.device_config_fk).delete() def dump_id(self) -> Dict: return {'device_uuid': {'uuid': self.device_uuid}} def dump_config(self) -> Dict: - return ConfigModel(self.database, self.device_config_fk).dump() + return self.device_config.dump() def dump_drivers(self) -> List[int]: - db_driver_pks = self.references(DriverModel) - return [DriverModel(self.database, pk).dump() for pk,_ in db_driver_pks] + return self.driver.dump() def dump_endpoints(self) -> List[Dict]: - from .EndPointModel import EndPointModel # pylint: disable=import-outside-toplevel - db_endpoints_pks = self.references(EndPointModel) - return [EndPointModel(self.database, pk).dump() for pk,_ in db_endpoints_pks] + return self.endpoints.dump() def dump( # pylint: disable=arguments-differ - self, include_config_rules=True, include_drivers=True, include_endpoints=True + self, include_config_rules=True, include_drivers=False, include_endpoints=False ) -> Dict: result = { 'device_id': self.dump_id(), @@ -100,16 +101,27 @@ class DeviceModel(Model): if include_endpoints: result['device_endpoints'] = self.dump_endpoints() return result -class DriverModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - device_fk = ForeignKeyField(DeviceModel) - driver = EnumeratedField(ORM_DeviceDriverEnum, required=True) + def main_pk_name(self): + return 'device_uuid' + +class DriverModel(Base): # pylint: disable=abstract-method + __tablename__ = 'Driver' + driver_uuid = Column(UUID(as_uuid=False), primary_key=True) + device_uuid = Column(UUID(as_uuid=False), ForeignKey("Device.device_uuid"), primary_key=True) + driver = Column(Enum(ORM_DeviceDriverEnum, create_constraint=False, native_enum=False)) + + # Relationships + device = relationship("DeviceModel") + def dump(self) -> Dict: return self.driver.value + def main_pk_name(self): + return 'driver_uuid' + def set_drivers(database : Database, db_device : DeviceModel, grpc_device_drivers): - db_device_pk = db_device.pk + db_device_pk = db_device.device_uuid for driver in grpc_device_drivers: orm_driver = grpc_to_enum__device_driver(driver) str_device_driver_key = key_to_str([db_device_pk, orm_driver.name]) diff --git a/src/context/service/database/EndPointModel.py b/src/context/service/database/EndPointModel.py index aeef91b65..669b590e3 100644 --- a/src/context/service/database/EndPointModel.py +++ b/src/context/service/database/EndPointModel.py @@ -17,24 +17,25 @@ from typing import Dict, List, Optional, Tuple from common.orm.Database import Database from common.orm.HighLevel import get_object from common.orm.backend.Tools import key_to_str -from common.orm.fields.EnumeratedField import EnumeratedField -from common.orm.fields.ForeignKeyField import ForeignKeyField -from common.orm.fields.PrimaryKeyField import PrimaryKeyField -from common.orm.fields.StringField import StringField -from common.orm.model.Model import Model from common.proto.context_pb2 import EndPointId -from .DeviceModel import DeviceModel from .KpiSampleType import ORM_KpiSampleTypeEnum, grpc_to_enum__kpi_sample_type -from .TopologyModel import TopologyModel - +from sqlalchemy import Column, ForeignKey, String, Enum, ForeignKeyConstraint +from sqlalchemy.dialects.postgresql import UUID, ARRAY +from context.service.database.Base import Base +from sqlalchemy.orm import relationship LOGGER = logging.getLogger(__name__) -class EndPointModel(Model): - pk = PrimaryKeyField() - topology_fk = ForeignKeyField(TopologyModel, required=False) - device_fk = ForeignKeyField(DeviceModel) - endpoint_uuid = StringField(required=True, allow_empty=False) - endpoint_type = StringField() +class EndPointModel(Base): + __tablename__ = 'EndPoint' + endpoint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) + topology_uuid = Column(UUID(as_uuid=False), ForeignKey("Topology.topology_uuid"), primary_key=True) + device_uuid = Column(UUID(as_uuid=False), ForeignKey("Device.device_uuid"), primary_key=True) + endpoint_type = Column(String) + + # Relationships + + def main_pk_name(self): + return 'endpoint_uuid' def delete(self) -> None: for db_kpi_sample_type_pk,_ in self.references(KpiSampleTypeModel): @@ -42,13 +43,10 @@ class EndPointModel(Model): super().delete() def dump_id(self) -> Dict: - device_id = DeviceModel(self.database, self.device_fk).dump_id() result = { - 'device_id': device_id, + 'device_uuid': self.device_uuid, 'endpoint_uuid': {'uuid': self.endpoint_uuid}, } - if self.topology_fk is not None: - result['topology_id'] = TopologyModel(self.database, self.topology_fk).dump_id() return result def dump_kpi_sample_types(self) -> List[int]: @@ -59,20 +57,26 @@ class EndPointModel(Model): self, include_kpi_sample_types=True ) -> Dict: result = { - 'endpoint_id': self.dump_id(), + 'endpoint_uuid': self.dump_id(), 'endpoint_type': self.endpoint_type, } if include_kpi_sample_types: result['kpi_sample_types'] = self.dump_kpi_sample_types() return result -class KpiSampleTypeModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - endpoint_fk = ForeignKeyField(EndPointModel) - kpi_sample_type = EnumeratedField(ORM_KpiSampleTypeEnum, required=True) - +class KpiSampleTypeModel(Base): # pylint: disable=abstract-method + __tablename__ = 'KpiSampleType' + kpi_uuid = Column(UUID(as_uuid=False), primary_key=True) + endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid")) + kpi_sample_type = Column(Enum(ORM_KpiSampleTypeEnum, create_constraint=False, + native_enum=False)) + # __table_args__ = (ForeignKeyConstraint([endpoint_uuid], [EndPointModel.endpoint_uuid]), {}) def dump(self) -> Dict: return self.kpi_sample_type.value + def main_pk_name(self): + return 'kpi_uuid' + +""" def set_kpi_sample_types(database : Database, db_endpoint : EndPointModel, grpc_endpoint_kpi_sample_types): db_endpoint_pk = db_endpoint.pk for kpi_sample_type in grpc_endpoint_kpi_sample_types: @@ -82,7 +86,7 @@ def set_kpi_sample_types(database : Database, db_endpoint : EndPointModel, grpc_ db_endpoint_kpi_sample_type.endpoint_fk = db_endpoint db_endpoint_kpi_sample_type.kpi_sample_type = orm_kpi_sample_type db_endpoint_kpi_sample_type.save() - +""" def get_endpoint( database : Database, grpc_endpoint_id : EndPointId, validate_topology_exists : bool = True, validate_device_in_topology : bool = True diff --git a/src/context/service/database/KpiSampleType.py b/src/context/service/database/KpiSampleType.py index 0a2015b3f..7f122f185 100644 --- a/src/context/service/database/KpiSampleType.py +++ b/src/context/service/database/KpiSampleType.py @@ -13,11 +13,11 @@ # limitations under the License. import functools -from enum import Enum +import enum from common.proto.kpi_sample_types_pb2 import KpiSampleType from .Tools import grpc_to_enum -class ORM_KpiSampleTypeEnum(Enum): +class ORM_KpiSampleTypeEnum(enum.Enum): UNKNOWN = KpiSampleType.KPISAMPLETYPE_UNKNOWN PACKETS_TRANSMITTED = KpiSampleType.KPISAMPLETYPE_PACKETS_TRANSMITTED PACKETS_RECEIVED = KpiSampleType.KPISAMPLETYPE_PACKETS_RECEIVED diff --git a/src/context/service/database/Tools.py b/src/context/service/database/Tools.py index 43bb71bd9..44a5aa264 100644 --- a/src/context/service/database/Tools.py +++ b/src/context/service/database/Tools.py @@ -15,8 +15,9 @@ import hashlib, re from enum import Enum from typing import Dict, List, Tuple, Union - +import logging # Convenient helper function to remove dictionary items in dict/list/set comprehensions. +LOGGER = logging.getLogger(__name__) def remove_dict_key(dictionary : Dict, key : str): dictionary.pop(key, None) diff --git a/src/context/service/database/TopologyModel.py b/src/context/service/database/TopologyModel.py index ec8427b07..2925a27fa 100644 --- a/src/context/service/database/TopologyModel.py +++ b/src/context/service/database/TopologyModel.py @@ -14,11 +14,6 @@ import logging, operator from typing import Dict, List -from common.orm.fields.ForeignKeyField import ForeignKeyField -from common.orm.fields.PrimaryKeyField import PrimaryKeyField -from common.orm.fields.StringField import StringField -from common.orm.model.Model import Model -from common.orm.HighLevel import get_related_objects from sqlalchemy.orm import relationship from sqlalchemy import Column, ForeignKey from sqlalchemy.dialects.postgresql import UUID @@ -28,10 +23,10 @@ LOGGER = logging.getLogger(__name__) class TopologyModel(Base): __tablename__ = 'Topology' context_uuid = Column(UUID(as_uuid=False), ForeignKey("Context.context_uuid"), primary_key=True) - topology_uuid = Column(UUID(as_uuid=False), primary_key=True) + topology_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) # Relationships - context = relationship("ContextModel", back_populates="topology", lazy="subquery") + context = relationship("ContextModel", back_populates="topology", lazy="joined") def dump_id(self) -> Dict: context_id = self.context.dump_id() @@ -40,6 +35,10 @@ class TopologyModel(Base): 'topology_uuid': {'uuid': self.topology_uuid}, } + @staticmethod + def main_pk_name() -> str: + return 'topology_uuid' + """def dump_device_ids(self) -> List[Dict]: from .RelationModels import TopologyDeviceModel # pylint: disable=import-outside-toplevel db_devices = get_related_objects(self, TopologyDeviceModel, 'device_fk') diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py index 5439b6c06..d104d5567 100644 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py @@ -11,9 +11,10 @@ # 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 uuid import grpc, json, logging, operator, threading -from typing import Iterator, List, Set, Tuple +from typing import Iterator, List, Set, Tuple, Union from common.message_broker.MessageBroker import MessageBroker from context.service.Database import Database @@ -25,19 +26,24 @@ from common.proto.context_pb2 import ( Link, LinkEvent, LinkId, LinkIdList, LinkList, Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList, Slice, SliceEvent, SliceId, SliceIdList, SliceList, - Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList) + Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList, + ConfigActionEnum) from common.proto.context_pb2_grpc import ContextServiceServicer from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException from sqlalchemy.orm import Session, contains_eager, selectinload from common.rpc_method_wrapper.ServiceExceptions import NotFoundException +from context.service.database.ConfigModel import grpc_config_rules_to_raw +from context.service.database.DeviceModel import DeviceModel, grpc_to_enum__device_operational_status, set_drivers, grpc_to_enum__device_driver, DriverModel +from context.service.database.ConfigModel import ConfigModel, ORM_ConfigActionEnum, ConfigRuleModel +from common.orm.backend.Tools import key_to_str + +from ..database.KpiSampleType import grpc_to_enum__kpi_sample_type """ -from context.service.database.ConfigModel import grpc_config_rules_to_raw, update_config from context.service.database.ConnectionModel import ConnectionModel, set_path from context.service.database.ConstraintModel import set_constraints -from context.service.database.DeviceModel import DeviceModel, grpc_to_enum__device_operational_status, set_drivers from context.service.database.EndPointModel import EndPointModel, set_kpi_sample_types from context.service.database.Events import notify_event from context.service.database.LinkModel import LinkModel @@ -51,8 +57,9 @@ from context.service.database.TopologyModel import TopologyModel """ from context.service.database.ContextModel import ContextModel from context.service.database.TopologyModel import TopologyModel -# from context.service.database.TopologyModel import TopologyModel from context.service.database.Events import notify_event +from context.service.database.EndPointModel import EndPointModel +from context.service.database.EndPointModel import KpiSampleTypeModel from .Constants import ( CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE, @@ -201,10 +208,10 @@ class ContextServiceServicerImpl(ContextServiceServicer): with self.session() as session: result = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).options(contains_eager(TopologyModel.context)).one_or_none() - if not result: - raise NotFoundException(TopologyModel.__name__.replace('Model', ''), topology_uuid) + if not result: + raise NotFoundException(TopologyModel.__name__.replace('Model', ''), topology_uuid) - return Topology(**result.dump()) + return Topology(**result.dump()) @safe_and_metered_rpc_method(METRICS, LOGGER) @@ -247,97 +254,201 @@ class ContextServiceServicerImpl(ContextServiceServicer): def GetTopologyEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[TopologyEvent]: for message in self.messagebroker.consume({TOPIC_TOPOLOGY}, consume_timeout=CONSUME_TIMEOUT): yield TopologyEvent(**json.loads(message.content)) - """ # ----- Device ----------------------------------------------------------------------------------------------------- @safe_and_metered_rpc_method(METRICS, LOGGER) def ListDeviceIds(self, request: Empty, context : grpc.ServicerContext) -> DeviceIdList: - with self.lock: - db_devices : List[DeviceModel] = get_all_objects(self.database, DeviceModel) - db_devices = sorted(db_devices, key=operator.attrgetter('pk')) - return DeviceIdList(device_ids=[db_device.dump_id() for db_device in db_devices]) + with self.session() as session: + result = session.query(DeviceModel).all() + return DeviceIdList(device_ids=[device.dump_id() for device in result]) @safe_and_metered_rpc_method(METRICS, LOGGER) def ListDevices(self, request: Empty, context : grpc.ServicerContext) -> DeviceList: - with self.lock: - db_devices : List[DeviceModel] = get_all_objects(self.database, DeviceModel) - db_devices = sorted(db_devices, key=operator.attrgetter('pk')) - return DeviceList(devices=[db_device.dump() for db_device in db_devices]) + with self.session() as session: + result = session.query(DeviceModel).all() + return DeviceList(devices=[device.dump_id() for device in result]) @safe_and_metered_rpc_method(METRICS, LOGGER) def GetDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Device: - with self.lock: - device_uuid = request.device_uuid.uuid - db_device : DeviceModel = get_object(self.database, DeviceModel, device_uuid) - return Device(**db_device.dump( - include_config_rules=True, include_drivers=True, include_endpoints=True)) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def SetDevice(self, request: Device, context : grpc.ServicerContext) -> DeviceId: - with self.lock: - device_uuid = request.device_id.device_uuid.uuid - - for i,endpoint in enumerate(request.device_endpoints): - endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid - if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid - if device_uuid != endpoint_device_uuid: - raise InvalidArgumentException( - 'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid, - ['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)]) - - config_rules = grpc_config_rules_to_raw(request.device_config.config_rules) - running_config_result = update_config(self.database, device_uuid, 'running', config_rules) - db_running_config = running_config_result[0][0] - - result : Tuple[DeviceModel, bool] = update_or_create_object(self.database, DeviceModel, device_uuid, { - 'device_uuid' : device_uuid, - 'device_type' : request.device_type, - 'device_operational_status': grpc_to_enum__device_operational_status(request.device_operational_status), - 'device_config_fk' : db_running_config, - }) - db_device, updated = result - - set_drivers(self.database, db_device, request.device_drivers) - - for i,endpoint in enumerate(request.device_endpoints): - endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid - endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid - if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid - - str_endpoint_key = key_to_str([device_uuid, endpoint_uuid]) - endpoint_attributes = { - 'device_fk' : db_device, - 'endpoint_uuid': endpoint_uuid, - 'endpoint_type': endpoint.endpoint_type, - } - - endpoint_topology_context_uuid = endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid - endpoint_topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid - if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: - str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) - db_topology : TopologyModel = get_object(self.database, TopologyModel, str_topology_key) - - str_topology_device_key = key_to_str([str_topology_key, device_uuid], separator='--') - result : Tuple[TopologyDeviceModel, bool] = get_or_create_object( - self.database, TopologyDeviceModel, str_topology_device_key, { - 'topology_fk': db_topology, 'device_fk': db_device}) - #db_topology_device, topology_device_created = result + device_uuid = request.device_uuid.uuid + with self.session() as session: + result = session.query(DeviceModel).filter(DeviceModel.device_uuid == device_uuid).one_or_none() + if not result: + raise NotFoundException(DeviceModel.__name__.replace('Model', ''), device_uuid) - str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') - endpoint_attributes['topology_fk'] = db_topology + rd = result.dump() + rt = Device(**rd) - result : Tuple[EndPointModel, bool] = update_or_create_object( - self.database, EndPointModel, str_endpoint_key, endpoint_attributes) - db_endpoint, endpoint_updated = result + return rt - set_kpi_sample_types(self.database, db_endpoint, endpoint.kpi_sample_types) + @safe_and_metered_rpc_method(METRICS, LOGGER) + def SetDevice(self, request: Device, context : grpc.ServicerContext) -> DeviceId: + device_uuid = request.device_id.device_uuid.uuid - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_device_id = db_device.dump_id() - notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id}) - return DeviceId(**dict_device_id) + for i,endpoint in enumerate(request.device_endpoints): + endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid + if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid + if device_uuid != endpoint_device_uuid: + raise InvalidArgumentException( + 'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid, + ['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)]) + + config_rules = grpc_config_rules_to_raw(request.device_config.config_rules) + running_config_result = self.update_config(device_uuid, 'running', config_rules) + db_running_config = running_config_result[0][0] + config_uuid = db_running_config.config_uuid + + new_obj = DeviceModel(**{ + 'device_uuid' : device_uuid, + 'device_type' : request.device_type, + 'device_operational_status' : grpc_to_enum__device_operational_status(request.device_operational_status), + 'device_config_uuid' : config_uuid, + }) + result: Tuple[DeviceModel, bool] = self.database.create_or_update(new_obj) + db_device, updated = result + + self.set_drivers(db_device, request.device_drivers) + + for i,endpoint in enumerate(request.device_endpoints): + endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid + endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid + if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid + + str_endpoint_key = key_to_str([device_uuid, endpoint_uuid]) + endpoint_attributes = { + 'device_uuid' : db_device.device_uuid, + 'endpoint_uuid': endpoint_uuid, + 'endpoint_type': endpoint.endpoint_type, + } + + endpoint_topology_context_uuid = endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid + endpoint_topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid + if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: + str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) + + db_topology : TopologyModel = self.database.get_object(TopologyModel, endpoint_topology_uuid) + + str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') + endpoint_attributes['topology_uuid'] = db_topology.topology_uuid + + new_endpoint = EndPointModel(**endpoint_attributes) + result : Tuple[EndPointModel, bool] = self.database.create_or_update(new_endpoint) + db_endpoint, updated = result + + self.set_kpi_sample_types(db_endpoint, endpoint.kpi_sample_types) + + # event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE + dict_device_id = db_device.dump_id() + # notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id}) + + return DeviceId(**dict_device_id) + + def set_kpi_sample_types(self, db_endpoint: EndPointModel, grpc_endpoint_kpi_sample_types): + db_endpoint_pk = db_endpoint.endpoint_uuid + for kpi_sample_type in grpc_endpoint_kpi_sample_types: + orm_kpi_sample_type = grpc_to_enum__kpi_sample_type(kpi_sample_type) + # str_endpoint_kpi_sample_type_key = key_to_str([db_endpoint_pk, orm_kpi_sample_type.name]) + data = {'endpoint_uuid': db_endpoint_pk, + 'kpi_sample_type': orm_kpi_sample_type.name, + 'kpi_uuid': str(uuid.uuid4())} + db_endpoint_kpi_sample_type = KpiSampleTypeModel(**data) + self.database.create(db_endpoint_kpi_sample_type) + + def set_drivers(self, db_device: DeviceModel, grpc_device_drivers): + db_device_pk = db_device.device_uuid + for driver in grpc_device_drivers: + orm_driver = grpc_to_enum__device_driver(driver) + str_device_driver_key = key_to_str([db_device_pk, orm_driver.name]) + driver_config = { + "driver_uuid": str(uuid.uuid4()), + "device_uuid": db_device_pk, + "driver": orm_driver.name + } + db_device_driver = DriverModel(**driver_config) + db_device_driver.device_fk = db_device + db_device_driver.driver = orm_driver + + self.database.create_or_update(db_device_driver) + + def update_config( + self, db_parent_pk: str, config_name: str, + raw_config_rules: List[Tuple[ORM_ConfigActionEnum, str, str]] + ) -> List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]]: + + str_config_key = key_to_str([db_parent_pk, config_name], separator=':') + result = self.database.get_or_create(ConfigModel, db_parent_pk) + db_config, created = result + + LOGGER.info('UPDATED-CONFIG: {}'.format(db_config.dump())) + + db_objects: List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]] = [(db_config, created)] + + for position, (action, resource_key, resource_value) in enumerate(raw_config_rules): + if action == ORM_ConfigActionEnum.SET: + result : Tuple[ConfigRuleModel, bool] = self.set_config_rule( + db_config, position, resource_key, resource_value) + db_config_rule, updated = result + db_objects.append((db_config_rule, updated)) + elif action == ORM_ConfigActionEnum.DELETE: + self.delete_config_rule(db_config, resource_key) + else: + msg = 'Unsupported action({:s}) for resource_key({:s})/resource_value({:s})' + raise AttributeError( + msg.format(str(ConfigActionEnum.Name(action)), str(resource_key), str(resource_value))) + + return db_objects + + def set_config_rule(self, db_config: ConfigModel, position: int, resource_key: str, resource_value: str, + ): # -> Tuple[ConfigRuleModel, bool]: + + from src.context.service.database.Tools import fast_hasher + str_rule_key_hash = fast_hasher(resource_key) + str_config_rule_key = key_to_str([db_config.config_uuid, str_rule_key_hash], separator=':') + pk = str(uuid.uuid5(uuid.UUID('9566448d-e950-425e-b2ae-7ead656c7e47'), str_config_rule_key)) + data = {'config_rule_uuid': pk, 'config_uuid': db_config.config_uuid, 'position': position, + 'action': ORM_ConfigActionEnum.SET, 'key': resource_key, 'value': resource_value} + to_add = ConfigRuleModel(**data) + + result, updated = self.database.create_or_update(to_add) + return result, updated + + def delete_config_rule( + self, db_config: ConfigModel, resource_key: str + ) -> None: + + from src.context.service.database.Tools import fast_hasher + str_rule_key_hash = fast_hasher(resource_key) + str_config_rule_key = key_to_str([db_config.pk, str_rule_key_hash], separator=':') + + db_config_rule = self.database.get_object(ConfigRuleModel, str_config_rule_key, raise_if_not_found=False) + + if db_config_rule is None: + return + db_config_rule.delete() + + def delete_all_config_rules(self, db_config: ConfigModel) -> None: + + db_config_rule_pks = db_config.references(ConfigRuleModel) + for pk, _ in db_config_rule_pks: ConfigRuleModel(self.database, pk).delete() + + """ + for position, (action, resource_key, resource_value) in enumerate(raw_config_rules): + if action == ORM_ConfigActionEnum.SET: + result: Tuple[ConfigRuleModel, bool] = set_config_rule( + database, db_config, position, resource_key, resource_value) + db_config_rule, updated = result + db_objects.append((db_config_rule, updated)) + elif action == ORM_ConfigActionEnum.DELETE: + delete_config_rule(database, db_config, resource_key) + else: + msg = 'Unsupported action({:s}) for resource_key({:s})/resource_value({:s})' + raise AttributeError( + msg.format(str(ConfigActionEnum.Name(action)), str(resource_key), str(resource_value))) + + return db_objects + """ @safe_and_metered_rpc_method(METRICS, LOGGER) def RemoveDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Empty: @@ -360,6 +471,9 @@ class ContextServiceServicerImpl(ContextServiceServicer): yield DeviceEvent(**json.loads(message.content)) + + """ + # ----- Link ------------------------------------------------------------------------------------------------------- @safe_and_metered_rpc_method(METRICS, LOGGER) diff --git a/src/context/tests/Objects.py b/src/context/tests/Objects.py index 519a0093a..772da38e0 100644 --- a/src/context/tests/Objects.py +++ b/src/context/tests/Objects.py @@ -45,12 +45,17 @@ PACKET_PORT_SAMPLE_TYPES = [ # ----- Device --------------------------------------------------------------------------------------------------------- -DEVICE_R1_UUID = 'R1' +EP2 = '7eb80584-2587-4e71-b10c-f3a5c48e84ab' +EP3 = '368baf47-0540-4ab4-add8-a19b5167162c' +EP100 = '6a923121-36e1-4b5e-8cd6-90aceca9b5cf' + + +DEVICE_R1_UUID = 'fe83a200-6ded-47b4-b156-3bb3556a10d6' DEVICE_R1_ID = json_device_id(DEVICE_R1_UUID) DEVICE_R1_EPS = [ - json_endpoint(DEVICE_R1_ID, 'EP2', '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), - json_endpoint(DEVICE_R1_ID, 'EP3', '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), - json_endpoint(DEVICE_R1_ID, 'EP100', '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), + json_endpoint(DEVICE_R1_ID, EP2, '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), + json_endpoint(DEVICE_R1_ID, EP3, '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), + json_endpoint(DEVICE_R1_ID, EP100, '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), ] DEVICE_R1_RULES = [ json_config_rule_set('dev/rsrc1/value', 'value1'), diff --git a/src/context/tests/test_unitary.py b/src/context/tests/test_unitary.py index e202de498..f238e95d9 100644 --- a/src/context/tests/test_unitary.py +++ b/src/context/tests/test_unitary.py @@ -20,7 +20,6 @@ from common.Settings import ( ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, ENVVAR_SUFIX_SERVICE_PORT_HTTP, get_env_var_name, get_service_baseurl_http, get_service_port_grpc, get_service_port_http) from context.service.Database import Database -from common.orm.Factory import get_database_backend, BackendEnum as DatabaseBackendEnum from common.message_broker.Factory import get_messagebroker_backend, BackendEnum as MessageBrokerBackendEnum from common.message_broker.MessageBroker import MessageBroker from common.proto.context_pb2 import ( @@ -84,7 +83,7 @@ def context_s_mb(request) -> Tuple[Session, MessageBroker]: msg = 'Running scenario {:s} db_session={:s}, mb_backend={:s}, mb_settings={:s}...' LOGGER.info(msg.format(str(name), str(db_session), str(mb_backend.value), str(mb_settings))) - db_uri = 'cockroachdb://root@10.152.183.66:26257/defaultdb?sslmode=disable' + db_uri = 'cockroachdb://root@10.152.183.111:26257/defaultdb?sslmode=disable' LOGGER.debug('Connecting to DB: {}'.format(db_uri)) try: @@ -95,7 +94,7 @@ def context_s_mb(request) -> Tuple[Session, MessageBroker]: return 1 Base.metadata.create_all(engine) - _session = sessionmaker(bind=engine) + _session = sessionmaker(bind=engine, expire_on_commit=False) _message_broker = MessageBroker(get_messagebroker_backend(backend=mb_backend, **mb_settings)) yield _session, _message_broker @@ -164,7 +163,7 @@ def test_grpc_context( assert len(response.contexts) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ - db_entries = database.query_all(ContextModel) + db_entries = database.get_all(ContextModel) LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) for db_entry in db_entries: LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover @@ -214,7 +213,7 @@ def test_grpc_context( assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Dump state of database after create/update the object ------------------------------------------------------ - db_entries = database.query_all(ContextModel) + db_entries = database.get_all(ContextModel) LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) # for db_entry in db_entries: @@ -252,7 +251,7 @@ def test_grpc_context( events_collector.stop() # ----- Dump state of database after remove the object ------------------------------------------------------------- - db_entries = database.query_all(ContextModel) + db_entries = database.get_all(ContextModel) LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) # for db_entry in db_entries: @@ -295,7 +294,7 @@ def test_grpc_topology( assert len(response.topologies) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ - db_entries = database.query_all(TopologyModel) + db_entries = database.get_all(TopologyModel) LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) # for db_entry in db_entries: # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover @@ -337,7 +336,7 @@ def test_grpc_topology( # assert event.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID # ----- Dump state of database after create/update the object ------------------------------------------------------ - db_entries = database.query_all(TopologyModel) + db_entries = database.get_all(TopologyModel) LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) # for db_entry in db_entries: # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover @@ -384,22 +383,22 @@ def test_grpc_topology( # events_collector.stop() # ----- Dump state of database after remove the object ------------------------------------------------------------- - db_entries = database.query_all(TopologyModel) + db_entries = database.get_all(TopologyModel) LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) # for db_entry in db_entries: # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 - """ - def test_grpc_device( - context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name - context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name - context_database = context_db_mb[0] + context_client_grpc: ContextClient, # pylint: disable=redefined-outer-name + context_s_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name + session = context_s_mb[0] + + database = Database(session) # ----- Clean the database ----------------------------------------------------------------------------------------- - context_database.clear_all() + database.clear() # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- events_collector = EventsCollector(context_client_grpc) @@ -438,49 +437,49 @@ def test_grpc_device( assert len(response.devices) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ - db_entries = context_database.dump() + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + # for db_entry in db_entries: + # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 5 + assert len(db_entries) == 2 # ----- Create the object ------------------------------------------------------------------------------------------ with pytest.raises(grpc.RpcError) as e: WRONG_DEVICE = copy.deepcopy(DEVICE_R1) - WRONG_DEVICE['device_endpoints'][0]['endpoint_id']['device_id']['device_uuid']['uuid'] = 'wrong-device-uuid' + WRONG_DEVICE_UUID = '3f03c76d-31fb-47f5-9c1d-bc6b6bfa2d08' + WRONG_DEVICE['device_endpoints'][0]['endpoint_id']['device_id']['device_uuid']['uuid'] = WRONG_DEVICE_UUID context_client_grpc.SetDevice(Device(**WRONG_DEVICE)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'request.device_endpoints[0].device_id.device_uuid.uuid(wrong-device-uuid) is invalid; '\ - 'should be == request.device_id.device_uuid.uuid({:s})'.format(DEVICE_R1_UUID) + msg = 'request.device_endpoints[0].device_id.device_uuid.uuid({}) is invalid; '\ + 'should be == request.device_id.device_uuid.uuid({})'.format(WRONG_DEVICE_UUID, DEVICE_R1_UUID) assert e.value.details() == msg - response = context_client_grpc.SetDevice(Device(**DEVICE_R1)) assert response.device_uuid.uuid == DEVICE_R1_UUID # ----- Check create event ----------------------------------------------------------------------------------------- - event = events_collector.get_event(block=True) - assert isinstance(event, DeviceEvent) - assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE - assert event.device_id.device_uuid.uuid == DEVICE_R1_UUID + # event = events_collector.get_event(block=True) + # assert isinstance(event, DeviceEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert event.device_id.device_uuid.uuid == DEVICE_R1_UUID # ----- Update the object ------------------------------------------------------------------------------------------ response = context_client_grpc.SetDevice(Device(**DEVICE_R1)) assert response.device_uuid.uuid == DEVICE_R1_UUID # ----- Check update event ----------------------------------------------------------------------------------------- - event = events_collector.get_event(block=True) - assert isinstance(event, DeviceEvent) - assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE - assert event.device_id.device_uuid.uuid == DEVICE_R1_UUID + # event = events_collector.get_event(block=True) + # assert isinstance(event, DeviceEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + # assert event.device_id.device_uuid.uuid == DEVICE_R1_UUID # ----- Dump state of database after create/update the object ------------------------------------------------------ - db_entries = context_database.dump() + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + # for db_entry in db_entries: + # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 40 + assert len(db_entries) == 36 # ----- Get when the object exists --------------------------------------------------------------------------------- response = context_client_grpc.GetDevice(DeviceId(**DEVICE_R1_ID)) @@ -513,11 +512,11 @@ def test_grpc_device( assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID # ----- Check update event ----------------------------------------------------------------------------------------- - event = events_collector.get_event(block=True) - assert isinstance(event, TopologyEvent) - assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE - assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + # event = events_collector.get_event(block=True) + # assert isinstance(event, TopologyEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + # assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID # ----- Check relation was created --------------------------------------------------------------------------------- response = context_client_grpc.GetTopology(TopologyId(**TOPOLOGY_ID)) @@ -528,12 +527,12 @@ def test_grpc_device( assert len(response.link_ids) == 0 # ----- Dump state of database after creating the object relation -------------------------------------------------- - db_entries = context_database.dump() + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + # for db_entry in db_entries: + # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 40 + assert len(db_entries) == 33 # ----- Remove the object ------------------------------------------------------------------------------------------ context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R1_ID)) @@ -541,33 +540,33 @@ def test_grpc_device( context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) # ----- Check remove event ----------------------------------------------------------------------------------------- - events = events_collector.get_events(block=True, count=3) + # events = events_collector.get_events(block=True, count=3) - assert isinstance(events[0], DeviceEvent) - assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - assert events[0].device_id.device_uuid.uuid == DEVICE_R1_UUID + # assert isinstance(events[0], DeviceEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[0].device_id.device_uuid.uuid == DEVICE_R1_UUID - assert isinstance(events[1], TopologyEvent) - assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - assert events[1].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert events[1].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + # assert isinstance(events[1], TopologyEvent) + # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[1].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert events[1].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID - assert isinstance(events[2], ContextEvent) - assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - assert events[2].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert isinstance(events[2], ContextEvent) + # assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[2].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- - events_collector.stop() + # events_collector.stop() # ----- Dump state of database after remove the object ------------------------------------------------------------- - db_entries = context_database.dump() + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + # for db_entry in db_entries: + # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 - + """ def test_grpc_link( context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name -- GitLab From 5beaeba93522007a38f817737a57737b8e30e68b Mon Sep 17 00:00:00 2001 From: Lluis Gifre Renom Date: Mon, 24 Oct 2022 15:04:05 +0000 Subject: [PATCH 044/353] Add New Feature template --- .gitlab/issue_templates/new-feature.md | 60 ++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .gitlab/issue_templates/new-feature.md diff --git a/.gitlab/issue_templates/new-feature.md b/.gitlab/issue_templates/new-feature.md new file mode 100644 index 000000000..ec0877164 --- /dev/null +++ b/.gitlab/issue_templates/new-feature.md @@ -0,0 +1,60 @@ +# Proposers + +- name-of-proposer-1 (institution-of-proposer-1) +- name-of-proposer-2 (institution-of-proposer-2) +... + +# Description + +Describe your proposal in ~1000 characters. +You can reference external content listed in section "References" as [Ref-1]. + +# Demo or definition of done + +Describe which high level conditions needs to be fulfilled to demonstrate this feature implementation is completed. +You can reference external content (example, demo paper) listed in section "References" as [Ref-2]. + +# References + +1. [Reference name](https://reference-url) +2. Author1, Author2, Author3, et. al., “My demo using feature,†in Conference-Name Demo Track, 20XX. + +# Feature Design (for New-Features) + +## Clarifications to Expected Behavior Changes + +Existing component logic and workflows between components that need to be altered to realize this feature. +Remember to justify these changes. +... + +## References + +List of relevant references for this feature. +... + +## Assumptions + +Enumerate the assumptions for this feature, e.g., fix XXX is implemented and merged, specific configurations, specific +components deployed. +... + +## Impacted Components + +List of impacted components: Context, Device, Service, PathComp, Slice, Monitoring, Automation, Policy, Compute, etc. +Just an enumeration, elaboration of impacts is done below. + +## Component1 Impact + +Describe impact (changes) on component1. +... + +## Component2 Impact + +Describe impact (changes) on component2. +... + +## Testing + +Describe test sets (unitary and integration) to be carried out. +This section can include/reference external experiments, demo papers, etc. +... -- GitLab From c3b9042f7beacb51ca8934ab1525049806ddb5a6 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Renom Date: Mon, 24 Oct 2022 15:05:38 +0000 Subject: [PATCH 045/353] Add Enhancement template --- .gitlab/issue_templates/enhancement.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .gitlab/issue_templates/enhancement.md diff --git a/.gitlab/issue_templates/enhancement.md b/.gitlab/issue_templates/enhancement.md new file mode 100644 index 000000000..06ea401cf --- /dev/null +++ b/.gitlab/issue_templates/enhancement.md @@ -0,0 +1,20 @@ +# Proposers + +- name-of-proposer-1 (institution-of-proposer-1) +- name-of-proposer-2 (institution-of-proposer-2) +... + +# Description + +Describe your proposal in ~1000 characters. +You can reference external content listed in section "References" as [Ref-1]. + +# Demo or definition of done + +Describe which high level conditions needs to be fulfilled to demonstrate this feature implementation is completed. +You can reference external content (example, demo paper) listed in section "References" as [Ref-2]. + +# References + +1. [Reference name](https://reference-url) +2. Author1, Author2, Author3, et. al., “My demo using feature,†in Conference-Name Demo Track, 20XX. -- GitLab From 3bab51359b98dc00d5ca077d6d590168d359181e Mon Sep 17 00:00:00 2001 From: Lluis Gifre Renom Date: Mon, 24 Oct 2022 15:06:51 +0000 Subject: [PATCH 046/353] Add New Feature with Design template --- .../new-feature-with-design.md | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .gitlab/issue_templates/new-feature-with-design.md diff --git a/.gitlab/issue_templates/new-feature-with-design.md b/.gitlab/issue_templates/new-feature-with-design.md new file mode 100644 index 000000000..ec0877164 --- /dev/null +++ b/.gitlab/issue_templates/new-feature-with-design.md @@ -0,0 +1,60 @@ +# Proposers + +- name-of-proposer-1 (institution-of-proposer-1) +- name-of-proposer-2 (institution-of-proposer-2) +... + +# Description + +Describe your proposal in ~1000 characters. +You can reference external content listed in section "References" as [Ref-1]. + +# Demo or definition of done + +Describe which high level conditions needs to be fulfilled to demonstrate this feature implementation is completed. +You can reference external content (example, demo paper) listed in section "References" as [Ref-2]. + +# References + +1. [Reference name](https://reference-url) +2. Author1, Author2, Author3, et. al., “My demo using feature,†in Conference-Name Demo Track, 20XX. + +# Feature Design (for New-Features) + +## Clarifications to Expected Behavior Changes + +Existing component logic and workflows between components that need to be altered to realize this feature. +Remember to justify these changes. +... + +## References + +List of relevant references for this feature. +... + +## Assumptions + +Enumerate the assumptions for this feature, e.g., fix XXX is implemented and merged, specific configurations, specific +components deployed. +... + +## Impacted Components + +List of impacted components: Context, Device, Service, PathComp, Slice, Monitoring, Automation, Policy, Compute, etc. +Just an enumeration, elaboration of impacts is done below. + +## Component1 Impact + +Describe impact (changes) on component1. +... + +## Component2 Impact + +Describe impact (changes) on component2. +... + +## Testing + +Describe test sets (unitary and integration) to be carried out. +This section can include/reference external experiments, demo papers, etc. +... -- GitLab From 7a7f0509f8cb9d2b19aee6edd88b691664295e7a Mon Sep 17 00:00:00 2001 From: Lluis Gifre Renom Date: Mon, 24 Oct 2022 15:07:11 +0000 Subject: [PATCH 047/353] Update new-feature.md --- .gitlab/issue_templates/new-feature.md | 40 -------------------------- 1 file changed, 40 deletions(-) diff --git a/.gitlab/issue_templates/new-feature.md b/.gitlab/issue_templates/new-feature.md index ec0877164..06ea401cf 100644 --- a/.gitlab/issue_templates/new-feature.md +++ b/.gitlab/issue_templates/new-feature.md @@ -18,43 +18,3 @@ You can reference external content (example, demo paper) listed in section "Refe 1. [Reference name](https://reference-url) 2. Author1, Author2, Author3, et. al., “My demo using feature,†in Conference-Name Demo Track, 20XX. - -# Feature Design (for New-Features) - -## Clarifications to Expected Behavior Changes - -Existing component logic and workflows between components that need to be altered to realize this feature. -Remember to justify these changes. -... - -## References - -List of relevant references for this feature. -... - -## Assumptions - -Enumerate the assumptions for this feature, e.g., fix XXX is implemented and merged, specific configurations, specific -components deployed. -... - -## Impacted Components - -List of impacted components: Context, Device, Service, PathComp, Slice, Monitoring, Automation, Policy, Compute, etc. -Just an enumeration, elaboration of impacts is done below. - -## Component1 Impact - -Describe impact (changes) on component1. -... - -## Component2 Impact - -Describe impact (changes) on component2. -... - -## Testing - -Describe test sets (unitary and integration) to be carried out. -This section can include/reference external experiments, demo papers, etc. -... -- GitLab From 0ec87ac80aec2c2e8a2c35de29f44b5e9260c767 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Renom Date: Mon, 7 Nov 2022 14:08:43 +0000 Subject: [PATCH 048/353] Add new template file for bugs --- .../.gitlab/issue_templates/bug.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .gitlab/issue_templates/.gitlab/issue_templates/bug.md diff --git a/.gitlab/issue_templates/.gitlab/issue_templates/bug.md b/.gitlab/issue_templates/.gitlab/issue_templates/bug.md new file mode 100644 index 000000000..3e9f22f83 --- /dev/null +++ b/.gitlab/issue_templates/.gitlab/issue_templates/bug.md @@ -0,0 +1,39 @@ +# Reporters + +- name-of-proposer-1 (institution-of-proposer-1) +- name-of-proposer-2 (institution-of-proposer-2) +... + +# Description + +Describe the bug. +If needed, Attach logs and reports to your bug. +If needed, feel free to point external content as references (journal paper, Youtube video, etc.). + +## Deployment environment +- Operating System (include version): +- MicroK8s (include version and add-ons): +- TeraFlowSDN (include release/branch-name/commit-id): + + +## TFS deployment settings +- list of components deployed +- particular configurations you applied +- any other particularity you might find important + +## Sequence of actions that resulted in the bug +- be explicit and enumerate each step. +- if possible, include example calls, request files, descriptor files, etc. + +## Document the explicit error +- include the traceback of the error +- include the log files of the components / scheenshoots of WebUI (attached files) +- if known, include the list of TFS components affected by the bug + +## Expected behaviour +- describe the expected behavior (if you know it) + +# References + +1. [Reference name](https://reference-url) +2. Author1, Author2, Author3, et. al., “My demo using feature,†in Conference-Name Demo Track, 20XX. -- GitLab From 6699e8b8671be098f67d80969c4bc464c7e19c27 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Renom Date: Mon, 7 Nov 2022 14:10:46 +0000 Subject: [PATCH 049/353] Update .gitlab/issue_templates/bug.md --- .gitlab/issue_templates/{.gitlab/issue_templates => }/bug.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .gitlab/issue_templates/{.gitlab/issue_templates => }/bug.md (100%) diff --git a/.gitlab/issue_templates/.gitlab/issue_templates/bug.md b/.gitlab/issue_templates/bug.md similarity index 100% rename from .gitlab/issue_templates/.gitlab/issue_templates/bug.md rename to .gitlab/issue_templates/bug.md -- GitLab From fbde6c023342795bb34129f3db99700faaa6e594 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Renom Date: Mon, 21 Nov 2022 10:47:53 +0000 Subject: [PATCH 050/353] Update README.md --- README.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5670a90c9..d05eb7446 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,22 @@ -# TeraFlow OS SDN Controller +# ETSI TeraFlowSDN Controller -[Teraflow H2020 project](https://teraflow-h2020.eu/) - Secured autonomic traffic management for a Tera of SDN Flows +[TeraFlow H2020 project](https://teraflow-h2020.eu/) - Secured autonomic traffic management for a Tera of SDN Flows +[ETSI TeraFlowSDN OSG](https://tfs.etsi.org/) - ETSI Open Source Group for TeraFlowSDN -Branch "master" : [![pipeline status](https://gitlab.com/teraflow-h2020/controller/badges/master/pipeline.svg)](https://gitlab.com/teraflow-h2020/controller/-/commits/master) [![coverage report](https://gitlab.com/teraflow-h2020/controller/badges/master/coverage.svg)](https://gitlab.com/teraflow-h2020/controller/-/commits/master) -Branch "develop" : [![pipeline status](https://gitlab.com/teraflow-h2020/controller/badges/develop/pipeline.svg)](https://gitlab.com/teraflow-h2020/controller/-/commits/develop) [![coverage report](https://gitlab.com/teraflow-h2020/controller/badges/develop/coverage.svg)](https://gitlab.com/teraflow-h2020/controller/-/commits/develop) +Branch "master" : [![pipeline status](https://labs.etsi.org/rep/tfs/controller/badges/master/pipeline.svg)](https://labs.etsi.org/rep/tfs/controller/-/commits/master) [![coverage report](https://labs.etsi.org/rep/tfs/controller/badges/master/coverage.svg)](https://labs.etsi.org/rep/tfs/controller/-/commits/master) + +Branch "develop" : [![pipeline status](https://labs.etsi.org/rep/tfs/controller/badges/develop/pipeline.svg)](https://labs.etsi.org/rep/tfs/controller/-/commits/develop) [![coverage report](https://labs.etsi.org/rep/tfs/controller/badges/develop/coverage.svg)](https://labs.etsi.org/rep/tfs/controller/-/commits/develop) + # Installation Instructions -To install TeraFlow OS SDN Controller in your local Kubernetes deployment, we assume you deployed Kubernetes following the instructions provided in [Wiki: Installing Kubernetes on your Linux machine](https://gitlab.com/teraflow-h2020/controller/-/wikis/Installing-Kubernetes-on-your-Linux-machine). +To install ETSI TeraFlowSDN Controller in your local Kubernetes deployment, we assume you deployed Kubernetes following the instructions provided in [Wiki: Installing Kubernetes on your Linux machine](https://gitlab.com/teraflow-h2020/controller/-/wikis/Installing-Kubernetes-on-your-Linux-machine). Then, follow the instructions in [Wiki: Deploying a TeraFlow OS test instance](https://gitlab.com/teraflow-h2020/controller/-/wikis/Deploying-a-TeraFlow-OS-test-instance) to deploy your instance of TeraFlow OS. + # Functional Tests -A functional test has been defined to enable experimentation with the TeraFlow OS: +A number of functional tests have been defined to enable experimentation with the ETSI TeraFlowSDN Controller: __Important:__ The OpenConfigDriver, the P4Driver, and the TrandportApiDriver have to be considered as experimental. The configuration and monitoring capabilities they support are limited or partially implemented. Use them with care. -- GitLab From 7b5224d4b910049a0310074020c48c482285cdc4 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Renom Date: Mon, 21 Nov 2022 10:50:44 +0000 Subject: [PATCH 051/353] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d05eb7446..ca386309d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # ETSI TeraFlowSDN Controller [TeraFlow H2020 project](https://teraflow-h2020.eu/) - Secured autonomic traffic management for a Tera of SDN Flows + [ETSI TeraFlowSDN OSG](https://tfs.etsi.org/) - ETSI Open Source Group for TeraFlowSDN -- GitLab From eee23b46ef4bd3c2250380510aa3e6a2e85bb646 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 23 Nov 2022 17:27:55 +0000 Subject: [PATCH 052/353] Performance Evaluation Method Wrapper - Initial version (not functional) --- .../perf_eval_method_wrapper/Decorator.py | 72 +++++++++++++++++++ .../perf_eval_method_wrapper/__init__.py | 14 ++++ .../tests/DummyDeviceDriver.py | 39 ++++++++++ .../tests/__init__.py | 14 ++++ .../tests/docker_grafana.sh | 22 ++++++ .../tests/prometheus.yml | 23 ++++++ .../tests/test_unitary.py | 62 ++++++++++++++++ 7 files changed, 246 insertions(+) create mode 100644 src/common/perf_eval_method_wrapper/Decorator.py create mode 100644 src/common/perf_eval_method_wrapper/__init__.py create mode 100644 src/common/perf_eval_method_wrapper/tests/DummyDeviceDriver.py create mode 100644 src/common/perf_eval_method_wrapper/tests/__init__.py create mode 100644 src/common/perf_eval_method_wrapper/tests/docker_grafana.sh create mode 100644 src/common/perf_eval_method_wrapper/tests/prometheus.yml create mode 100644 src/common/perf_eval_method_wrapper/tests/test_unitary.py diff --git a/src/common/perf_eval_method_wrapper/Decorator.py b/src/common/perf_eval_method_wrapper/Decorator.py new file mode 100644 index 000000000..2f5c9b952 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/Decorator.py @@ -0,0 +1,72 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 threading +from enum import Enum +from typing import Dict, Tuple +from prometheus_client import Counter, Histogram +from prometheus_client.metrics import MetricWrapperBase, INF + +class MetricTypeEnum(Enum): + COUNTER_STARTED = '{:s}_counter_requests_started' + COUNTER_COMPLETED = '{:s}_counter_requests_completed' + COUNTER_FAILED = '{:s}_counter_requests_failed' + HISTOGRAM_DURATION = '{:s}_histogram_duration' + +METRIC_TO_CLASS_PARAMS = { + MetricTypeEnum.COUNTER_STARTED : (Counter, {}), + MetricTypeEnum.COUNTER_COMPLETED : (Counter, {}), + MetricTypeEnum.COUNTER_FAILED : (Counter, {}), + MetricTypeEnum.HISTOGRAM_DURATION: (Histogram, { + 'buckets': (.005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, INF) + }) +} + +class MetricsPool: + def __init__(self) -> None: + self._metrics : Dict[str, MetricWrapperBase] = dict() + self._lock = threading.Lock() + + def get_or_create(self, function_name : str, metric_type : MetricTypeEnum, **metric_params) -> MetricWrapperBase: + metric_name = str(metric_type.value).format(function_name).upper() + with self._lock: + if metric_name not in self._metrics: + metric_tuple : Tuple[MetricWrapperBase, Dict] = METRIC_TO_CLASS_PARAMS.get(metric_type) + metric_class, default_metric_params = metric_tuple + if len(metric_params) == 0: metric_params = default_metric_params + self._metrics[metric_name] = metric_class(metric_name, '', **metric_params) + return self._metrics[metric_name] + +def meter_method(metrics_pool : MetricsPool): + def outer_wrapper(func): + func_name = func.__name__ + histogram_duration : Histogram = metrics_pool.get_or_create(func_name, MetricTypeEnum.HISTOGRAM_DURATION) + counter_started : Counter = metrics_pool.get_or_create(func_name, MetricTypeEnum.COUNTER_STARTED) + counter_completed : Counter = metrics_pool.get_or_create(func_name, MetricTypeEnum.COUNTER_COMPLETED) + counter_failed : Counter = metrics_pool.get_or_create(func_name, MetricTypeEnum.COUNTER_FAILED) + + @histogram_duration.time() + def inner_wrapper(self, *args, **kwargs): + counter_started.inc() + try: + reply = func(self, *args, **kwargs) + counter_completed.inc() + return reply + except KeyboardInterrupt: # pylint: disable=try-except-raise + raise + except Exception: # pylint: disable=broad-except + counter_failed.inc() + + return inner_wrapper + return outer_wrapper diff --git a/src/common/perf_eval_method_wrapper/__init__.py b/src/common/perf_eval_method_wrapper/__init__.py new file mode 100644 index 000000000..70a332512 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/common/perf_eval_method_wrapper/tests/DummyDeviceDriver.py b/src/common/perf_eval_method_wrapper/tests/DummyDeviceDriver.py new file mode 100644 index 000000000..b44c830d9 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/DummyDeviceDriver.py @@ -0,0 +1,39 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 random, time +from common.perf_eval_method_wrapper.Decorator import MetricsPool, meter_method + +EXCEPTION_RATIO = 0.05 + +METRICS_POOL = MetricsPool() + +class DummyDeviceDriver: + def __init__(self) -> None: + pass + + @meter_method(METRICS_POOL) + def get_config(self): + if random.random() < EXCEPTION_RATIO: raise Exception() + time.sleep(random.random()) + + @meter_method(METRICS_POOL) + def set_config(self): + if random.random() < EXCEPTION_RATIO: raise Exception() + time.sleep(random.random()) + + @meter_method(METRICS_POOL) + def del_config(self): + if random.random() < EXCEPTION_RATIO: raise Exception() + time.sleep(random.random()) diff --git a/src/common/perf_eval_method_wrapper/tests/__init__.py b/src/common/perf_eval_method_wrapper/tests/__init__.py new file mode 100644 index 000000000..70a332512 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/common/perf_eval_method_wrapper/tests/docker_grafana.sh b/src/common/perf_eval_method_wrapper/tests/docker_grafana.sh new file mode 100644 index 000000000..2a1484d55 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/docker_grafana.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + + +docker create -it --name=prometheus -p 9090:9090 \ + -v /home/tfs/tfs-ctrl/test-prom-cli/prometheus.yml:/etc/prometheus/prometheus.yml \ + prom/prometheus + +docker create -it --name=grafana -p 3000:3000 \ + grafana/grafana diff --git a/src/common/perf_eval_method_wrapper/tests/prometheus.yml b/src/common/perf_eval_method_wrapper/tests/prometheus.yml new file mode 100644 index 000000000..af2849209 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/prometheus.yml @@ -0,0 +1,23 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +global: + scrape_interval: 15s # when Prometheus is pulling data from exporters etc + evaluation_interval: 30s # time between each evaluation of Prometheus' alerting rules + +scrape_configs: +- job_name: ddd # your project name + static_configs: + - targets: + - 172.17.0.1:8000 diff --git a/src/common/perf_eval_method_wrapper/tests/test_unitary.py b/src/common/perf_eval_method_wrapper/tests/test_unitary.py new file mode 100644 index 000000000..e34f75016 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/test_unitary.py @@ -0,0 +1,62 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 grpc, logging, time +from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method + +logging.basicConfig(level=logging.DEBUG) +LOGGER = logging.getLogger(__name__) + +def test_database_instantiation(): + SERVICE_NAME = 'Context' + METHOD_NAMES = [ + 'ListContextIds', 'ListContexts', 'GetContext', 'SetContext', 'RemoveContext', 'GetContextEvents', + 'ListTopologyIds', 'ListTopologies', 'GetTopology', 'SetTopology', 'RemoveTopology', 'GetTopologyEvents', + 'ListDeviceIds', 'ListDevices', 'GetDevice', 'SetDevice', 'RemoveDevice', 'GetDeviceEvents', + 'ListLinkIds', 'ListLinks', 'GetLink', 'SetLink', 'RemoveLink', 'GetLinkEvents', + 'ListServiceIds', 'ListServices', 'GetService', 'SetService', 'RemoveService', 'GetServiceEvents', + ] + METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) + + class TestServiceServicerImpl: + @safe_and_metered_rpc_method(METRICS, LOGGER) + def GetTopology(self, request, grpc_context : grpc.ServicerContext): + print('doing funny things') + time.sleep(0.1) + return 'done' + + tssi = TestServiceServicerImpl() + tssi.GetTopology(1, 2) + + for metric_name,metric in METRICS.items(): + if 'GETTOPOLOGY_' not in metric_name: continue + print(metric_name, metric._child_samples()) # pylint: disable=protected-access + + + +import random +from prometheus_client import start_http_server +from DummyDeviceDriver import DummyDeviceDriver + +def main(): + # Start up the server to expose the metrics + start_http_server(8000) + + ddd = DummyDeviceDriver() + while True: + func = random.choice([ddd.get_config, ddd.set_config, ddd.del_config]) + func() + +if __name__ == '__main__': + main() -- GitLab From e902afdff9830ce447a300254a8329191a070acf Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 24 Nov 2022 14:43:06 +0000 Subject: [PATCH 053/353] Common: - Updated deploy.sh script to handle service monitor CRDs --- deploy.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/deploy.sh b/deploy.sh index fa1dc2b36..d9b1d69d6 100755 --- a/deploy.sh +++ b/deploy.sh @@ -234,7 +234,11 @@ done echo "Deploying extra manifests..." for EXTRA_MANIFEST in $TFS_EXTRA_MANIFESTS; do echo "Processing manifest '$EXTRA_MANIFEST'..." - kubectl --namespace $TFS_K8S_NAMESPACE apply -f $EXTRA_MANIFEST + if [[ "$EXTRA_MANIFEST" == *"servicemonitor"* ]]; then + kubectl apply -f $EXTRA_MANIFEST + else + kubectl --namespace $TFS_K8S_NAMESPACE apply -f $EXTRA_MANIFEST + fi printf "\n" done printf "\n" -- GitLab From 8c957489536c3218536eee4d775440d192e161a9 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 24 Nov 2022 14:43:47 +0000 Subject: [PATCH 054/353] Manifests: - updated manifests for context, device, service, slice to expose metrics endpoint --- manifests/contextservice.yaml | 7 +++++++ manifests/deviceservice.yaml | 8 ++++++++ manifests/serviceservice.yaml | 8 ++++++++ manifests/sliceservice.yaml | 8 ++++++++ 4 files changed, 31 insertions(+) diff --git a/manifests/contextservice.yaml b/manifests/contextservice.yaml index 5c07971a3..299864032 100644 --- a/manifests/contextservice.yaml +++ b/manifests/contextservice.yaml @@ -45,6 +45,7 @@ spec: ports: - containerPort: 1010 - containerPort: 8080 + - containerPort: 9192 env: - name: DB_BACKEND value: "redis" @@ -74,6 +75,8 @@ apiVersion: v1 kind: Service metadata: name: contextservice + labels: + app: contextservice spec: type: ClusterIP selector: @@ -87,3 +90,7 @@ spec: protocol: TCP port: 8080 targetPort: 8080 + - name: metrics + protocol: TCP + port: 9192 + targetPort: 9192 diff --git a/manifests/deviceservice.yaml b/manifests/deviceservice.yaml index d2595ab19..83daa41f3 100644 --- a/manifests/deviceservice.yaml +++ b/manifests/deviceservice.yaml @@ -20,6 +20,7 @@ spec: selector: matchLabels: app: deviceservice + replicas: 1 template: metadata: labels: @@ -32,6 +33,7 @@ spec: imagePullPolicy: Always ports: - containerPort: 2020 + - containerPort: 9192 env: - name: LOG_LEVEL value: "INFO" @@ -53,6 +55,8 @@ apiVersion: v1 kind: Service metadata: name: deviceservice + labels: + app: deviceservice spec: type: ClusterIP selector: @@ -62,3 +66,7 @@ spec: protocol: TCP port: 2020 targetPort: 2020 + - name: metrics + protocol: TCP + port: 9192 + targetPort: 9192 diff --git a/manifests/serviceservice.yaml b/manifests/serviceservice.yaml index a5568a511..d9ecc998e 100644 --- a/manifests/serviceservice.yaml +++ b/manifests/serviceservice.yaml @@ -20,6 +20,7 @@ spec: selector: matchLabels: app: serviceservice + replicas: 1 template: metadata: labels: @@ -32,6 +33,7 @@ spec: imagePullPolicy: Always ports: - containerPort: 3030 + - containerPort: 9192 env: - name: LOG_LEVEL value: "INFO" @@ -53,6 +55,8 @@ apiVersion: v1 kind: Service metadata: name: serviceservice + labels: + app: serviceservice spec: type: ClusterIP selector: @@ -62,3 +66,7 @@ spec: protocol: TCP port: 3030 targetPort: 3030 + - name: metrics + protocol: TCP + port: 9192 + targetPort: 9192 diff --git a/manifests/sliceservice.yaml b/manifests/sliceservice.yaml index b20669b0c..ff4b41fe7 100644 --- a/manifests/sliceservice.yaml +++ b/manifests/sliceservice.yaml @@ -20,6 +20,7 @@ spec: selector: matchLabels: app: sliceservice + replicas: 1 template: metadata: labels: @@ -32,6 +33,7 @@ spec: imagePullPolicy: Always ports: - containerPort: 4040 + - containerPort: 9192 env: - name: LOG_LEVEL value: "INFO" @@ -53,6 +55,8 @@ apiVersion: v1 kind: Service metadata: name: sliceservice + labels: + app: sliceservice spec: type: ClusterIP selector: @@ -62,3 +66,7 @@ spec: protocol: TCP port: 4040 targetPort: 4040 + - name: metrics + protocol: TCP + port: 9192 + targetPort: 9192 -- GitLab From 8d87d0aebf114abbdaf40fbffab4f201e56f7a53 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 24 Nov 2022 14:49:17 +0000 Subject: [PATCH 055/353] Performance Evaluation: - defined README.md file with commands - created example dashboard - defined deploy specs - defined service monitors for context, device, service, slice components --- .../perf_eval_method_wrapper/tests/README.md | 32 +++++ .../tests/dashboard_prometheus_histogram.json | 110 +++++++++++++++++ .../tests/deploy_specs.sh | 26 ++++ .../tests/servicemonitors.yaml | 115 ++++++++++++++++++ 4 files changed, 283 insertions(+) create mode 100644 src/common/perf_eval_method_wrapper/tests/README.md create mode 100644 src/common/perf_eval_method_wrapper/tests/dashboard_prometheus_histogram.json create mode 100644 src/common/perf_eval_method_wrapper/tests/deploy_specs.sh create mode 100644 src/common/perf_eval_method_wrapper/tests/servicemonitors.yaml diff --git a/src/common/perf_eval_method_wrapper/tests/README.md b/src/common/perf_eval_method_wrapper/tests/README.md new file mode 100644 index 000000000..93eba4d81 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/README.md @@ -0,0 +1,32 @@ +# Performance Evaluation Method Wrapper + +- deploy as: +``` +tfs@tfs-vm:~/tfs-ctrl$ source src/common/perf_eval_method_wrapper/tests/deploy_specs.sh +tfs@tfs-vm:~/tfs-ctrl$ ./deploy.sh +``` + +- expose prometheus and grafana +terminal 1 (prometheus UI): +``` +kubectl port-forward -n monitoring service/prometheus-k8s --address 0.0.0.0 9090:9090 +``` + +terminal 2 (prometheus internal grafana): +``` +kubectl port-forward -n monitoring service/grafana --address 0.0.0.0 3000:3000 +``` + +terminal 3 (alertmanager): +``` +kubectl port-forward -n monitoring service/alertmanager-main --address 0.0.0.0 9093:9093 +``` + +- log into grafana: + - 127.0.0.1:3000 + - admin/admin + - upload dashboard_prometheus_histogram.json + - watch in real time the dashboard + +- upload topology through WebUI and navigate + - should see histogram changing in Grafana diff --git a/src/common/perf_eval_method_wrapper/tests/dashboard_prometheus_histogram.json b/src/common/perf_eval_method_wrapper/tests/dashboard_prometheus_histogram.json new file mode 100644 index 000000000..21aa91146 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/dashboard_prometheus_histogram.json @@ -0,0 +1,110 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 25, + "links": [], + "panels": [ + { + "cards": { + "cardPadding": null, + "cardRound": null + }, + "color": { + "cardColor": "#b4ff00", + "colorScale": "sqrt", + "colorScheme": "interpolateRdYlGn", + "exponent": 0.5, + "max": null, + "min": 0, + "mode": "opacity" + }, + "dataFormat": "tsbuckets", + "datasource": "prometheus", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 11, + "w": 24, + "x": 0, + "y": 0 + }, + "heatmap": {}, + "hideZeroBuckets": false, + "highlightCards": true, + "id": 2, + "legend": { + "show": true + }, + "pluginVersion": "7.5.4", + "reverseYBuckets": false, + "targets": [ + { + "exemplar": true, + "expr": "sum(increase(Context_GetDevice_histogram_duration_bucket[$__interval])) by (le)", + "format": "heatmap", + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{le}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "TFS / Histogram Component RPCs", + "tooltip": { + "show": true, + "showHistogram": true + }, + "type": "heatmap", + "xAxis": { + "show": true + }, + "xBucketNumber": null, + "xBucketSize": null, + "yAxis": { + "decimals": null, + "format": "s", + "logBase": 1, + "max": null, + "min": null, + "show": true, + "splitFactor": null + }, + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null + } + ], + "refresh": "5s", + "schemaVersion": 27, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "TFS / Histogram Component RPCs", + "uid": "eAg-wsOVk", + "version": 2 +} diff --git a/src/common/perf_eval_method_wrapper/tests/deploy_specs.sh b/src/common/perf_eval_method_wrapper/tests/deploy_specs.sh new file mode 100644 index 000000000..797b646e2 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/deploy_specs.sh @@ -0,0 +1,26 @@ +# Set the URL of your local Docker registry where the images will be uploaded to. +export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" + +# Set the list of components, separated by spaces, you want to build images for, and deploy. +# Supported components are: +# context device automation policy service compute monitoring webui +# interdomain slice pathcomp dlt +# dbscanserving opticalattackmitigator opticalattackdetector +# l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector +export TFS_COMPONENTS="context device pathcomp service slice webui" + +# Set the tag you want to use for your images. +export TFS_IMAGE_TAG="dev" + +# Set the name of the Kubernetes namespace to deploy to. +export TFS_K8S_NAMESPACE="tfs" + +# Set additional manifest files to be applied after the deployment +export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml src/common/perf_eval_method_wrapper/tests/servicemonitors.yaml" + +# Set the new Grafana admin password +export TFS_GRAFANA_PASSWORD="admin123+" + +# If not already set, disable skip-build flag. +# If TFS_SKIP_BUILD is "YES", the containers are not rebuilt-retagged-repushed and existing ones are used. +export TFS_SKIP_BUILD=${TFS_SKIP_BUILD:-"YES"} diff --git a/src/common/perf_eval_method_wrapper/tests/servicemonitors.yaml b/src/common/perf_eval_method_wrapper/tests/servicemonitors.yaml new file mode 100644 index 000000000..403657101 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/servicemonitors.yaml @@ -0,0 +1,115 @@ +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + namespace: monitoring # namespace where prometheus is running + name: tfs-contextservice-metric + labels: + app: contextservice + #release: prometheus + #release: prom # name of the release + # ( VERY IMPORTANT: You need to know the correct release name by viewing + # the servicemonitor of Prometheus itself: Without the correct name, + # Prometheus cannot identify the metrics of the Flask app as the target.) +spec: + selector: + matchLabels: + # Target app service + #namespace: tfs + app: contextservice # same as above + #release: prometheus # same as above + endpoints: + - port: metrics # named port in target app + scheme: http + path: /metrics # path to scrape + interval: 15s # scrape interval + namespaceSelector: + any: false + matchNames: + - tfs # namespace where the app is running +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + namespace: monitoring # namespace where prometheus is running + name: tfs-deviceservice-metric + labels: + app: deviceservice + #release: prometheus + #release: prom # name of the release + # ( VERY IMPORTANT: You need to know the correct release name by viewing + # the servicemonitor of Prometheus itself: Without the correct name, + # Prometheus cannot identify the metrics of the Flask app as the target.) +spec: + selector: + matchLabels: + # Target app service + #namespace: tfs + app: deviceservice # same as above + #release: prometheus # same as above + endpoints: + - port: metrics # named port in target app + scheme: http + path: /metrics # path to scrape + interval: 15s # scrape interval + namespaceSelector: + any: false + matchNames: + - tfs # namespace where the app is running +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + namespace: monitoring # namespace where prometheus is running + name: tfs-serviceservice-metric + labels: + app: serviceservice + #release: prometheus + #release: prom # name of the release + # ( VERY IMPORTANT: You need to know the correct release name by viewing + # the servicemonitor of Prometheus itself: Without the correct name, + # Prometheus cannot identify the metrics of the Flask app as the target.) +spec: + selector: + matchLabels: + # Target app service + #namespace: tfs + app: serviceservice # same as above + #release: prometheus # same as above + endpoints: + - port: metrics # named port in target app + scheme: http + path: /metrics # path to scrape + interval: 15s # scrape interval + namespaceSelector: + any: false + matchNames: + - tfs # namespace where the app is running +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + namespace: monitoring # namespace where prometheus is running + name: tfs-sliceservice-metric + labels: + app: sliceservice + #release: prometheus + #release: prom # name of the release + # ( VERY IMPORTANT: You need to know the correct release name by viewing + # the servicemonitor of Prometheus itself: Without the correct name, + # Prometheus cannot identify the metrics of the Flask app as the target.) +spec: + selector: + matchLabels: + # Target app service + #namespace: tfs + app: sliceservice # same as above + #release: prometheus # same as above + endpoints: + - port: metrics # named port in target app + scheme: http + path: /metrics # path to scrape + interval: 15s # scrape interval + namespaceSelector: + any: false + matchNames: + - tfs # namespace where the app is running -- GitLab From 82631dc9bf8d97e3e4e57e253b4d86ab0fb7daf6 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 24 Nov 2022 15:12:49 +0000 Subject: [PATCH 056/353] Performance Evaluation: - updated README.md file with useful references --- .../perf_eval_method_wrapper/tests/README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/common/perf_eval_method_wrapper/tests/README.md b/src/common/perf_eval_method_wrapper/tests/README.md index 93eba4d81..26597e798 100644 --- a/src/common/perf_eval_method_wrapper/tests/README.md +++ b/src/common/perf_eval_method_wrapper/tests/README.md @@ -1,5 +1,7 @@ # Performance Evaluation Method Wrapper +## Description: + - deploy as: ``` tfs@tfs-vm:~/tfs-ctrl$ source src/common/perf_eval_method_wrapper/tests/deploy_specs.sh @@ -30,3 +32,15 @@ kubectl port-forward -n monitoring service/alertmanager-main --address 0.0.0.0 9 - upload topology through WebUI and navigate - should see histogram changing in Grafana + +## References: +- [Prometheus - Tutorials - Getting Started](https://prometheus.io/docs/tutorials/getting_started/) +- [Prometheus - Tutorials - Understanding metric types](https://prometheus.io/docs/tutorials/understanding_metric_types/) +- [Prometheus - Tutorials - Instrumenting HTTP server in Go](https://prometheus.io/docs/tutorials/instrumenting_http_server_in_go/) +- [Prometheus - Tutorials - Visualizing metrics using Grafana](https://prometheus.io/docs/tutorials/visualizing_metrics_using_grafana/) +- [Prometheus - Tutorials - Alerting based on metrics](https://prometheus.io/docs/tutorials/alerting_based_on_metrics/) +- [Prometheus Operator - Guide](https://www.infracloud.io/blogs/prometheus-operator-helm-guide/) +- [Prometheus Operator - ServiceMonitor definition](https://prometheus-operator.dev/docs/operator/api/#monitoring.coreos.com/v1.ServiceMonitor) +- [Prometheus Operator - ServiceMonitor example 1](https://stackoverflow.com/questions/45613660/how-do-you-add-scrape-targets-to-a-prometheus-server-that-was-installed-with-kub) +- [Prometheus Operator - ServiceMonitor example 2](https://stackoverflow.com/questions/52991038/how-to-create-a-servicemonitor-for-prometheus-operator) +- [How to visualize Prometheus histograms in Grafana](https://grafana.com/blog/2020/06/23/how-to-visualize-prometheus-histograms-in-grafana/) -- GitLab From 3637f806db78338346259a66845328f46e59eb4d Mon Sep 17 00:00:00 2001 From: Lluis Gifre Renom Date: Thu, 24 Nov 2022 15:26:50 +0000 Subject: [PATCH 057/353] Update src/common/perf_eval_method_wrapper/tests/README.md --- src/common/perf_eval_method_wrapper/tests/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/common/perf_eval_method_wrapper/tests/README.md b/src/common/perf_eval_method_wrapper/tests/README.md index 26597e798..ff06b6b31 100644 --- a/src/common/perf_eval_method_wrapper/tests/README.md +++ b/src/common/perf_eval_method_wrapper/tests/README.md @@ -2,6 +2,12 @@ ## Description: +- enable prometheus addon: +``` +tfs@tfs-vm:~/tfs-ctrl$ microk8s.enable prometheus +tfs@tfs-vm:~/tfs-ctrl$ microk8s.status --wait-ready +``` + - deploy as: ``` tfs@tfs-vm:~/tfs-ctrl$ source src/common/perf_eval_method_wrapper/tests/deploy_specs.sh -- GitLab From edd54e9195ca33aec8b60f9c63e3a747807fac23 Mon Sep 17 00:00:00 2001 From: Lluis Gifre Renom Date: Thu, 24 Nov 2022 15:29:07 +0000 Subject: [PATCH 058/353] Update src/common/perf_eval_method_wrapper/tests/README.md --- .../perf_eval_method_wrapper/tests/README.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/common/perf_eval_method_wrapper/tests/README.md b/src/common/perf_eval_method_wrapper/tests/README.md index ff06b6b31..060af8a3e 100644 --- a/src/common/perf_eval_method_wrapper/tests/README.md +++ b/src/common/perf_eval_method_wrapper/tests/README.md @@ -15,20 +15,12 @@ tfs@tfs-vm:~/tfs-ctrl$ ./deploy.sh ``` - expose prometheus and grafana -terminal 1 (prometheus UI): -``` -kubectl port-forward -n monitoring service/prometheus-k8s --address 0.0.0.0 9090:9090 -``` -terminal 2 (prometheus internal grafana): -``` -kubectl port-forward -n monitoring service/grafana --address 0.0.0.0 3000:3000 -``` + - terminal 1 (prometheus UI): `kubectl port-forward -n monitoring service/prometheus-k8s --address 0.0.0.0 9090:9090` + - terminal 2 (grafana UI): `kubectl port-forward -n monitoring service/grafana --address 0.0.0.0 3000:3000` + - terminal 3 (alertmanager UI): `kubectl port-forward -n monitoring service/alertmanager-main --address 0.0.0.0 9093:9093` -terminal 3 (alertmanager): -``` -kubectl port-forward -n monitoring service/alertmanager-main --address 0.0.0.0 9093:9093 -``` +- if using remote server/VM for running MicroK8s and VSCode, forward ports 9090, 3000, 9093 - log into grafana: - 127.0.0.1:3000 -- GitLab From de520e7c6cba9989d10310cf5dbacfc091c85061 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Mon, 28 Nov 2022 15:59:28 +0000 Subject: [PATCH 059/353] Common - Perf Eval Decorator - updated deploy specs script - added missing commands in README.md - renamed test launcher - added support for labels --- .../perf_eval_method_wrapper/Decorator.py | 17 +++-- .../tests/DummyDeviceDriver.py | 2 +- .../perf_eval_method_wrapper/tests/README.md | 6 ++ .../tests/__main__.py | 32 ++++++++++ .../tests/deploy_specs.sh | 2 +- .../tests/test_unitary.py | 62 ------------------- 6 files changed, 51 insertions(+), 70 deletions(-) create mode 100644 src/common/perf_eval_method_wrapper/tests/__main__.py delete mode 100644 src/common/perf_eval_method_wrapper/tests/test_unitary.py diff --git a/src/common/perf_eval_method_wrapper/Decorator.py b/src/common/perf_eval_method_wrapper/Decorator.py index 2f5c9b952..6cd3a0cef 100644 --- a/src/common/perf_eval_method_wrapper/Decorator.py +++ b/src/common/perf_eval_method_wrapper/Decorator.py @@ -34,10 +34,13 @@ METRIC_TO_CLASS_PARAMS = { } class MetricsPool: - def __init__(self) -> None: + def __init__(self, labels : Dict[str, str] = {}) -> None: + self._labels = labels self._metrics : Dict[str, MetricWrapperBase] = dict() self._lock = threading.Lock() + def get_labels(self) -> Dict[str, str]: return self._labels + def get_or_create(self, function_name : str, metric_type : MetricTypeEnum, **metric_params) -> MetricWrapperBase: metric_name = str(metric_type.value).format(function_name).upper() with self._lock: @@ -45,28 +48,30 @@ class MetricsPool: metric_tuple : Tuple[MetricWrapperBase, Dict] = METRIC_TO_CLASS_PARAMS.get(metric_type) metric_class, default_metric_params = metric_tuple if len(metric_params) == 0: metric_params = default_metric_params - self._metrics[metric_name] = metric_class(metric_name, '', **metric_params) + labels = sorted(self._labels.keys()) + self._metrics[metric_name] = metric_class(metric_name, '', labels, **metric_params) return self._metrics[metric_name] def meter_method(metrics_pool : MetricsPool): def outer_wrapper(func): func_name = func.__name__ + labels = metrics_pool.get_labels() histogram_duration : Histogram = metrics_pool.get_or_create(func_name, MetricTypeEnum.HISTOGRAM_DURATION) counter_started : Counter = metrics_pool.get_or_create(func_name, MetricTypeEnum.COUNTER_STARTED) counter_completed : Counter = metrics_pool.get_or_create(func_name, MetricTypeEnum.COUNTER_COMPLETED) counter_failed : Counter = metrics_pool.get_or_create(func_name, MetricTypeEnum.COUNTER_FAILED) - @histogram_duration.time() + @histogram_duration.labels(**labels).time() def inner_wrapper(self, *args, **kwargs): - counter_started.inc() + counter_started.labels(**labels).inc() try: reply = func(self, *args, **kwargs) - counter_completed.inc() + counter_completed.labels(**labels).inc() return reply except KeyboardInterrupt: # pylint: disable=try-except-raise raise except Exception: # pylint: disable=broad-except - counter_failed.inc() + counter_failed.labels(**labels).inc() return inner_wrapper return outer_wrapper diff --git a/src/common/perf_eval_method_wrapper/tests/DummyDeviceDriver.py b/src/common/perf_eval_method_wrapper/tests/DummyDeviceDriver.py index b44c830d9..f4fe1169e 100644 --- a/src/common/perf_eval_method_wrapper/tests/DummyDeviceDriver.py +++ b/src/common/perf_eval_method_wrapper/tests/DummyDeviceDriver.py @@ -17,7 +17,7 @@ from common.perf_eval_method_wrapper.Decorator import MetricsPool, meter_method EXCEPTION_RATIO = 0.05 -METRICS_POOL = MetricsPool() +METRICS_POOL = MetricsPool(labels={'driver': 'dummy'}) class DummyDeviceDriver: def __init__(self) -> None: diff --git a/src/common/perf_eval_method_wrapper/tests/README.md b/src/common/perf_eval_method_wrapper/tests/README.md index 26597e798..908ffcea1 100644 --- a/src/common/perf_eval_method_wrapper/tests/README.md +++ b/src/common/perf_eval_method_wrapper/tests/README.md @@ -24,6 +24,12 @@ terminal 3 (alertmanager): kubectl port-forward -n monitoring service/alertmanager-main --address 0.0.0.0 9093:9093 ``` +terminal 4 (tun test_set): +``` +export PYTHONPATH=/home/tfs/tfs-ctrl/src +python -m common.perf_eval_method_wrapper.tests.test_set +``` + - log into grafana: - 127.0.0.1:3000 - admin/admin diff --git a/src/common/perf_eval_method_wrapper/tests/__main__.py b/src/common/perf_eval_method_wrapper/tests/__main__.py new file mode 100644 index 000000000..505991f61 --- /dev/null +++ b/src/common/perf_eval_method_wrapper/tests/__main__.py @@ -0,0 +1,32 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, random +from prometheus_client import start_http_server +from .DummyDeviceDriver import DummyDeviceDriver + +logging.basicConfig(level=logging.DEBUG) +LOGGER = logging.getLogger(__name__) + +def main(): + # Start up the server to expose the metrics + start_http_server(8000) + + ddd = DummyDeviceDriver() + while True: + func = random.choice([ddd.get_config, ddd.set_config, ddd.del_config]) + func() + +if __name__ == '__main__': + main() diff --git a/src/common/perf_eval_method_wrapper/tests/deploy_specs.sh b/src/common/perf_eval_method_wrapper/tests/deploy_specs.sh index 797b646e2..f7b7f3578 100644 --- a/src/common/perf_eval_method_wrapper/tests/deploy_specs.sh +++ b/src/common/perf_eval_method_wrapper/tests/deploy_specs.sh @@ -23,4 +23,4 @@ export TFS_GRAFANA_PASSWORD="admin123+" # If not already set, disable skip-build flag. # If TFS_SKIP_BUILD is "YES", the containers are not rebuilt-retagged-repushed and existing ones are used. -export TFS_SKIP_BUILD=${TFS_SKIP_BUILD:-"YES"} +export TFS_SKIP_BUILD="NO" #${TFS_SKIP_BUILD:-"YES"} diff --git a/src/common/perf_eval_method_wrapper/tests/test_unitary.py b/src/common/perf_eval_method_wrapper/tests/test_unitary.py deleted file mode 100644 index e34f75016..000000000 --- a/src/common/perf_eval_method_wrapper/tests/test_unitary.py +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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 grpc, logging, time -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method - -logging.basicConfig(level=logging.DEBUG) -LOGGER = logging.getLogger(__name__) - -def test_database_instantiation(): - SERVICE_NAME = 'Context' - METHOD_NAMES = [ - 'ListContextIds', 'ListContexts', 'GetContext', 'SetContext', 'RemoveContext', 'GetContextEvents', - 'ListTopologyIds', 'ListTopologies', 'GetTopology', 'SetTopology', 'RemoveTopology', 'GetTopologyEvents', - 'ListDeviceIds', 'ListDevices', 'GetDevice', 'SetDevice', 'RemoveDevice', 'GetDeviceEvents', - 'ListLinkIds', 'ListLinks', 'GetLink', 'SetLink', 'RemoveLink', 'GetLinkEvents', - 'ListServiceIds', 'ListServices', 'GetService', 'SetService', 'RemoveService', 'GetServiceEvents', - ] - METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) - - class TestServiceServicerImpl: - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetTopology(self, request, grpc_context : grpc.ServicerContext): - print('doing funny things') - time.sleep(0.1) - return 'done' - - tssi = TestServiceServicerImpl() - tssi.GetTopology(1, 2) - - for metric_name,metric in METRICS.items(): - if 'GETTOPOLOGY_' not in metric_name: continue - print(metric_name, metric._child_samples()) # pylint: disable=protected-access - - - -import random -from prometheus_client import start_http_server -from DummyDeviceDriver import DummyDeviceDriver - -def main(): - # Start up the server to expose the metrics - start_http_server(8000) - - ddd = DummyDeviceDriver() - while True: - func = random.choice([ddd.get_config, ddd.set_config, ddd.del_config]) - func() - -if __name__ == '__main__': - main() -- GitLab From 6b24659462c94de0e83fec33d6c5e94329a3260f Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Mon, 28 Nov 2022 16:00:03 +0000 Subject: [PATCH 060/353] Device - EmulatedDriver: - first try of decorating device driver --- src/device/service/drivers/emulated/EmulatedDriver.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/device/service/drivers/emulated/EmulatedDriver.py b/src/device/service/drivers/emulated/EmulatedDriver.py index 2ee9a10ca..94da07bfa 100644 --- a/src/device/service/drivers/emulated/EmulatedDriver.py +++ b/src/device/service/drivers/emulated/EmulatedDriver.py @@ -19,6 +19,7 @@ from apscheduler.executors.pool import ThreadPoolExecutor from apscheduler.job import Job from apscheduler.jobstores.memory import MemoryJobStore from apscheduler.schedulers.background import BackgroundScheduler +from common.perf_eval_method_wrapper.Decorator import MetricsPool, meter_method from common.type_checkers.Checkers import chk_float, chk_length, chk_string, chk_type from device.service.database.KpiSampleType import ORM_KpiSampleTypeEnum, grpc_to_enum__kpi_sample_type from device.service.driver_api._Driver import ( @@ -122,6 +123,8 @@ def do_sampling( value = abs(0.95 * waveform + 0.05 * noise) out_samples.put_nowait((timestamp, resource_key, value)) +METRICS_POOL = MetricsPool(labels={'driver': 'emulated'}) + class EmulatedDriver(_Driver): def __init__(self, address : str, port : int, **settings) -> None: # pylint: disable=super-init-not-called self.__lock = threading.Lock() @@ -170,10 +173,12 @@ class EmulatedDriver(_Driver): self.__scheduler.shutdown() return True + @meter_method(METRICS_POOL) def GetInitialConfig(self) -> List[Tuple[str, Any]]: with self.__lock: return dump_subtree(self.__initial) + @meter_method(METRICS_POOL) def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]: chk_type('resources', resource_keys, list) with self.__lock: @@ -197,6 +202,7 @@ class EmulatedDriver(_Driver): results.extend(dump_subtree(resource_node)) return results + @meter_method(METRICS_POOL) def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] @@ -231,6 +237,7 @@ class EmulatedDriver(_Driver): results.append(True) return results + @meter_method(METRICS_POOL) def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] @@ -268,6 +275,7 @@ class EmulatedDriver(_Driver): results.append(True) return results + @meter_method(METRICS_POOL) def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: chk_type('subscriptions', subscriptions, list) if len(subscriptions) == 0: return [] @@ -305,6 +313,7 @@ class EmulatedDriver(_Driver): results.append(True) return results + @meter_method(METRICS_POOL) def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: chk_type('subscriptions', subscriptions, list) if len(subscriptions) == 0: return [] -- GitLab From 0482f489b0cd26c701dee9589803570779217db6 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 30 Nov 2022 18:37:34 +0000 Subject: [PATCH 061/353] Common: - unified RPC method wrapper with performance evaluation - migrated components to new method wrapping API - added new grafana dashboard backups --- src/common/method_wrappers/Decorator.py | 127 ++++++ .../ServiceExceptions.py | 0 .../__init__.py | 0 .../tests/DummyDeviceDriver.py | 0 .../tests/README.md | 7 +- .../tests/__init__.py | 0 .../tests/__main__.py | 0 .../tests/deploy_specs.sh | 4 +- .../grafana_prometheus_component_rpc.json | 0 .../grafana_prometheus_device_driver.json | 394 ++++++++++++++++++ .../grafana_prometheus_service_handler.json | 394 ++++++++++++++++++ .../tests/old}/docker_grafana.sh | 0 .../tests/old}/prometheus.yml | 0 .../tests/prometheus_queries.txt | 31 ++ .../tests/servicemonitors.yaml | 231 ++++++++++ .../tests/test_unitary.py | 22 +- src/common/orm/HighLevel.py | 2 +- .../perf_eval_method_wrapper/Decorator.py | 77 ---- .../tests/dashboard_prometheus_histogram.json | 110 ----- .../tests/servicemonitors.yaml | 115 ----- src/common/rpc_method_wrapper/Decorator.py | 82 ---- src/common/rpc_method_wrapper/__init__.py | 14 - .../rpc_method_wrapper/tests/__init__.py | 14 - .../service/ComputeServiceServicerImpl.py | 23 +- .../grpc_server/ContextServiceServicerImpl.py | 114 +++-- .../service/DbscanServiceServicerImpl.py | 8 +- .../service/DeviceServiceServicerImpl.py | 18 +- src/device/service/database/DatabaseTools.py | 2 +- .../drivers/emulated/EmulatedDriver.py | 16 +- .../drivers/microwave/IETFApiDriver.py | 9 + .../drivers/openconfig/OpenConfigDriver.py | 9 + src/device/service/drivers/p4/p4_driver.py | 8 + .../transport_api/TransportApiDriver.py | 9 + src/device/service/drivers/xr/XrDriver.py | 8 + .../DltConnectorServiceServicerImpl.py | 30 +- .../service/InterdomainServiceServicerImpl.py | 16 +- .../InterdomainServiceServicerImpl.py | 16 +- src/monitoring/service/EventTools.py | 2 +- .../service/MonitoringServiceServicerImpl.py | 2 +- ...ticalAttackMitigatorServiceServicerImpl.py | 8 +- ...alizedAttackDetectorServiceServicerImpl.py | 14 +- .../service/PathCompServiceServicerImpl.py | 8 +- .../service/ServiceServiceServicerImpl.py | 14 +- .../L2NMEmulatedServiceHandler.py | 9 + .../L3NMEmulatedServiceHandler.py | 9 + .../L3NMOpenConfigServiceHandler.py | 9 + .../microwave/MicrowaveServiceHandler.py | 9 + .../tapi_tapi/TapiServiceHandler.py | 9 + .../service/task_scheduler/TaskExecutor.py | 2 +- .../tasks/Task_ConnectionConfigure.py | 2 +- .../tasks/Task_ConnectionDeconfigure.py | 2 +- src/slice/service/SliceServiceServicerImpl.py | 12 +- 52 files changed, 1412 insertions(+), 609 deletions(-) create mode 100644 src/common/method_wrappers/Decorator.py rename src/common/{rpc_method_wrapper => method_wrappers}/ServiceExceptions.py (100%) rename src/common/{perf_eval_method_wrapper => method_wrappers}/__init__.py (100%) rename src/common/{perf_eval_method_wrapper => method_wrappers}/tests/DummyDeviceDriver.py (100%) rename src/common/{perf_eval_method_wrapper => method_wrappers}/tests/README.md (88%) rename src/common/{perf_eval_method_wrapper => method_wrappers}/tests/__init__.py (100%) rename src/common/{perf_eval_method_wrapper => method_wrappers}/tests/__main__.py (100%) rename src/common/{perf_eval_method_wrapper => method_wrappers}/tests/deploy_specs.sh (87%) create mode 100644 src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json create mode 100644 src/common/method_wrappers/tests/grafana_prometheus_device_driver.json create mode 100644 src/common/method_wrappers/tests/grafana_prometheus_service_handler.json rename src/common/{perf_eval_method_wrapper/tests => method_wrappers/tests/old}/docker_grafana.sh (100%) rename src/common/{perf_eval_method_wrapper/tests => method_wrappers/tests/old}/prometheus.yml (100%) create mode 100644 src/common/method_wrappers/tests/prometheus_queries.txt create mode 100644 src/common/method_wrappers/tests/servicemonitors.yaml rename src/common/{rpc_method_wrapper => method_wrappers}/tests/test_unitary.py (50%) delete mode 100644 src/common/perf_eval_method_wrapper/Decorator.py delete mode 100644 src/common/perf_eval_method_wrapper/tests/dashboard_prometheus_histogram.json delete mode 100644 src/common/perf_eval_method_wrapper/tests/servicemonitors.yaml delete mode 100644 src/common/rpc_method_wrapper/Decorator.py delete mode 100644 src/common/rpc_method_wrapper/__init__.py delete mode 100644 src/common/rpc_method_wrapper/tests/__init__.py diff --git a/src/common/method_wrappers/Decorator.py b/src/common/method_wrappers/Decorator.py new file mode 100644 index 000000000..c7ddfa231 --- /dev/null +++ b/src/common/method_wrappers/Decorator.py @@ -0,0 +1,127 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 grpc, logging, threading +from enum import Enum +from typing import Dict, Tuple +from prometheus_client import Counter, Histogram +from prometheus_client.metrics import MetricWrapperBase, INF +from common.tools.grpc.Tools import grpc_message_to_json_string +from .ServiceExceptions import ServiceException + +class MetricTypeEnum(Enum): + COUNTER_STARTED = 'tfs_{component:s}_{sub_module:s}_{method:s}_counter_requests_started' + COUNTER_COMPLETED = 'tfs_{component:s}_{sub_module:s}_{method:s}_counter_requests_completed' + COUNTER_FAILED = 'tfs_{component:s}_{sub_module:s}_{method:s}_counter_requests_failed' + HISTOGRAM_DURATION = 'tfs_{component:s}_{sub_module:s}_{method:s}_histogram_duration' + +METRIC_TO_CLASS_PARAMS = { + MetricTypeEnum.COUNTER_STARTED : (Counter, {}), + MetricTypeEnum.COUNTER_COMPLETED : (Counter, {}), + MetricTypeEnum.COUNTER_FAILED : (Counter, {}), + MetricTypeEnum.HISTOGRAM_DURATION: (Histogram, { + 'buckets': ( + # .005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, INF + 0.001, 0.002, 0.003, 0.004, 0.005, 0.0075, + 0.010, 0.025, 0.050, 0.075, + 0.100, 0.250, 0.500, 0.750, + 1.000, 2.500, 5.000, 7.500, + 10.00, INF) + }) +} + +class MetricsPool: + lock = threading.Lock() + metrics : Dict[str, MetricWrapperBase] = dict() + + def __init__(self, component : str, sub_module : str, labels : Dict[str, str] = {}) -> None: + self._component = component + self._sub_module = sub_module + self._labels = labels + + def get_or_create(self, method : str, metric_type : MetricTypeEnum, **metric_params) -> MetricWrapperBase: + metric_name = str(metric_type.value).format( + component=self._component, sub_module=self._sub_module, method=method).upper() + with MetricsPool.lock: + if metric_name not in MetricsPool.metrics: + metric_tuple : Tuple[MetricWrapperBase, Dict] = METRIC_TO_CLASS_PARAMS.get(metric_type) + metric_class, default_metric_params = metric_tuple + if len(metric_params) == 0: metric_params = default_metric_params + labels = sorted(self._labels.keys()) + MetricsPool.metrics[metric_name] = metric_class(metric_name.lower(), '', labels, **metric_params) + return MetricsPool.metrics[metric_name] + + def get_metrics( + self, method : str + ) -> Tuple[MetricWrapperBase, MetricWrapperBase, MetricWrapperBase, MetricWrapperBase]: + histogram_duration : Histogram = self.get_or_create(method, MetricTypeEnum.HISTOGRAM_DURATION) + counter_started : Counter = self.get_or_create(method, MetricTypeEnum.COUNTER_STARTED) + counter_completed : Counter = self.get_or_create(method, MetricTypeEnum.COUNTER_COMPLETED) + counter_failed : Counter = self.get_or_create(method, MetricTypeEnum.COUNTER_FAILED) + + if len(self._labels) > 0: + histogram_duration = histogram_duration.labels(**(self._labels)) + counter_started = counter_started.labels(**(self._labels)) + counter_completed = counter_completed.labels(**(self._labels)) + counter_failed = counter_failed.labels(**(self._labels)) + + return histogram_duration, counter_started, counter_completed, counter_failed + +def metered_subclass_method(metrics_pool : MetricsPool): + def outer_wrapper(func): + metrics = metrics_pool.get_metrics(func.__name__) + histogram_duration, counter_started, counter_completed, counter_failed = metrics + + @histogram_duration.time() + def inner_wrapper(self, *args, **kwargs): + counter_started.inc() + try: + reply = func(self, *args, **kwargs) + counter_completed.inc() + return reply + except KeyboardInterrupt: # pylint: disable=try-except-raise + raise + except Exception: # pylint: disable=broad-except + counter_failed.inc() + + return inner_wrapper + return outer_wrapper + +def safe_and_metered_rpc_method(metrics_pool : MetricsPool, logger : logging.Logger): + def outer_wrapper(func): + method_name = func.__name__ + metrics = metrics_pool.get_metrics(method_name) + histogram_duration, counter_started, counter_completed, counter_failed = metrics + + @histogram_duration.time() + def inner_wrapper(self, request, grpc_context : grpc.ServicerContext): + counter_started.inc() + try: + logger.debug('{:s} request: {:s}'.format(method_name, grpc_message_to_json_string(request))) + reply = func(self, request, grpc_context) + logger.debug('{:s} reply: {:s}'.format(method_name, grpc_message_to_json_string(reply))) + counter_completed.inc() + return reply + except ServiceException as e: # pragma: no cover (ServiceException not thrown) + if e.code not in [grpc.StatusCode.NOT_FOUND, grpc.StatusCode.ALREADY_EXISTS]: + # Assume not found or already exists is just a condition, not an error + logger.exception('{:s} exception'.format(method_name)) + counter_failed.inc() + grpc_context.abort(e.code, e.details) + except Exception as e: # pragma: no cover, pylint: disable=broad-except + logger.exception('{:s} exception'.format(method_name)) + counter_failed.inc() + grpc_context.abort(grpc.StatusCode.INTERNAL, str(e)) + return inner_wrapper + return outer_wrapper diff --git a/src/common/rpc_method_wrapper/ServiceExceptions.py b/src/common/method_wrappers/ServiceExceptions.py similarity index 100% rename from src/common/rpc_method_wrapper/ServiceExceptions.py rename to src/common/method_wrappers/ServiceExceptions.py diff --git a/src/common/perf_eval_method_wrapper/__init__.py b/src/common/method_wrappers/__init__.py similarity index 100% rename from src/common/perf_eval_method_wrapper/__init__.py rename to src/common/method_wrappers/__init__.py diff --git a/src/common/perf_eval_method_wrapper/tests/DummyDeviceDriver.py b/src/common/method_wrappers/tests/DummyDeviceDriver.py similarity index 100% rename from src/common/perf_eval_method_wrapper/tests/DummyDeviceDriver.py rename to src/common/method_wrappers/tests/DummyDeviceDriver.py diff --git a/src/common/perf_eval_method_wrapper/tests/README.md b/src/common/method_wrappers/tests/README.md similarity index 88% rename from src/common/perf_eval_method_wrapper/tests/README.md rename to src/common/method_wrappers/tests/README.md index 1e29da3d6..b0db20bd9 100644 --- a/src/common/perf_eval_method_wrapper/tests/README.md +++ b/src/common/method_wrappers/tests/README.md @@ -10,14 +10,14 @@ tfs@tfs-vm:~/tfs-ctrl$ microk8s.status --wait-ready - deploy as: ``` -tfs@tfs-vm:~/tfs-ctrl$ source src/common/perf_eval_method_wrapper/tests/deploy_specs.sh +tfs@tfs-vm:~/tfs-ctrl$ source src/common/method_wrappers/tests/deploy_specs.sh tfs@tfs-vm:~/tfs-ctrl$ ./deploy.sh ``` - expose prometheus and grafana - terminal 1 (prometheus UI): `kubectl port-forward -n monitoring service/prometheus-k8s --address 0.0.0.0 9090:9090` - - terminal 2 (grafana UI): `kubectl port-forward -n monitoring service/grafana --address 0.0.0.0 3000:3000` + - terminal 2 (grafana UI): `kubectl port-forward -n monitoring service/grafana --address 0.0.0.0 3001:3000` - terminal 3 (alertmanager UI): `kubectl port-forward -n monitoring service/alertmanager-main --address 0.0.0.0 9093:9093` - if using remote server/VM for running MicroK8s and VSCode, forward ports 9090, 3000, 9093 @@ -25,7 +25,7 @@ tfs@tfs-vm:~/tfs-ctrl$ ./deploy.sh terminal 4 (tun test_set): ``` export PYTHONPATH=/home/tfs/tfs-ctrl/src -python -m common.perf_eval_method_wrapper.tests.test_set +python -m common.method_wrappers.tests.test_set ``` - log into grafana: @@ -48,3 +48,4 @@ python -m common.perf_eval_method_wrapper.tests.test_set - [Prometheus Operator - ServiceMonitor example 1](https://stackoverflow.com/questions/45613660/how-do-you-add-scrape-targets-to-a-prometheus-server-that-was-installed-with-kub) - [Prometheus Operator - ServiceMonitor example 2](https://stackoverflow.com/questions/52991038/how-to-create-a-servicemonitor-for-prometheus-operator) - [How to visualize Prometheus histograms in Grafana](https://grafana.com/blog/2020/06/23/how-to-visualize-prometheus-histograms-in-grafana/) +- [Prometheus Histograms with Grafana Heatmaps](https://towardsdatascience.com/prometheus-histograms-with-grafana-heatmaps-d556c28612c7) diff --git a/src/common/perf_eval_method_wrapper/tests/__init__.py b/src/common/method_wrappers/tests/__init__.py similarity index 100% rename from src/common/perf_eval_method_wrapper/tests/__init__.py rename to src/common/method_wrappers/tests/__init__.py diff --git a/src/common/perf_eval_method_wrapper/tests/__main__.py b/src/common/method_wrappers/tests/__main__.py similarity index 100% rename from src/common/perf_eval_method_wrapper/tests/__main__.py rename to src/common/method_wrappers/tests/__main__.py diff --git a/src/common/perf_eval_method_wrapper/tests/deploy_specs.sh b/src/common/method_wrappers/tests/deploy_specs.sh similarity index 87% rename from src/common/perf_eval_method_wrapper/tests/deploy_specs.sh rename to src/common/method_wrappers/tests/deploy_specs.sh index f7b7f3578..3e4883251 100644 --- a/src/common/perf_eval_method_wrapper/tests/deploy_specs.sh +++ b/src/common/method_wrappers/tests/deploy_specs.sh @@ -7,7 +7,7 @@ export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" # interdomain slice pathcomp dlt # dbscanserving opticalattackmitigator opticalattackdetector # l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector -export TFS_COMPONENTS="context device pathcomp service slice webui" +export TFS_COMPONENTS="context device monitoring pathcomp service slice compute webui" # automation # Set the tag you want to use for your images. export TFS_IMAGE_TAG="dev" @@ -16,7 +16,7 @@ export TFS_IMAGE_TAG="dev" export TFS_K8S_NAMESPACE="tfs" # Set additional manifest files to be applied after the deployment -export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml src/common/perf_eval_method_wrapper/tests/servicemonitors.yaml" +export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml src/common/method_wrappers/tests/servicemonitors.yaml" # Set the new Grafana admin password export TFS_GRAFANA_PASSWORD="admin123+" diff --git a/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json b/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json new file mode 100644 index 000000000..e69de29bb diff --git a/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json b/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json new file mode 100644 index 000000000..32d4c0d59 --- /dev/null +++ b/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json @@ -0,0 +1,394 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 25, + "iteration": 1669828816450, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "prometheus", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.4", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "device_driver_[[method]]_counter_requests_started_total{driver=\"[[driver]]\"}", + "interval": "", + "legendFormat": "{{__name__}}", + "queryType": "randomWalk", + "refId": "A" + }, + { + "exemplar": true, + "expr": "device_driver_[[method]]_counter_requests_completed_total{driver=\"[[driver]]\"}", + "hide": false, + "interval": "", + "legendFormat": "{{__name__}}", + "refId": "B" + }, + { + "exemplar": true, + "expr": "device_driver_[[method]]_counter_requests_failed_total{driver=\"[[driver]]\"}", + "hide": false, + "interval": "", + "legendFormat": "{{__name__}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transformations": [ + { + "id": "renameByRegex", + "options": { + "regex": "device_.*_counter_requests_(.*)_total", + "renamePattern": "$1" + } + } + ], + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:864", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:865", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "prometheus", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 6 + }, + "hiddenSeries": false, + "id": 5, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.4", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "device_driver_[[method]]_histogram_duration_sum{driver=\"[[driver]]\"}", + "hide": false, + "interval": "", + "legendFormat": "total time", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Total Exec Time", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transformations": [], + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:407", + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:408", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "cards": { + "cardPadding": null, + "cardRound": null + }, + "color": { + "cardColor": "#b4ff00", + "colorScale": "sqrt", + "colorScheme": "interpolateRdYlGn", + "exponent": 0.5, + "max": null, + "min": 0, + "mode": "opacity" + }, + "dataFormat": "tsbuckets", + "datasource": "prometheus", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 12 + }, + "heatmap": {}, + "hideZeroBuckets": true, + "highlightCards": true, + "id": 2, + "legend": { + "show": false + }, + "pluginVersion": "7.5.4", + "reverseYBuckets": false, + "targets": [ + { + "exemplar": true, + "expr": "sum(increase(device_driver_[[method]]_histogram_duration_bucket{driver=\"[[driver]]\"}[$__rate_interval])) by (le)", + "format": "heatmap", + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{le}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Histogram", + "tooltip": { + "show": true, + "showHistogram": true + }, + "type": "heatmap", + "xAxis": { + "show": true + }, + "xBucketNumber": null, + "xBucketSize": null, + "yAxis": { + "decimals": null, + "format": "s", + "logBase": 1, + "max": null, + "min": null, + "show": true, + "splitFactor": null + }, + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null + } + ], + "refresh": "5s", + "schemaVersion": 27, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": { + "selected": false, + "text": "setconfig", + "value": "setconfig" + }, + "datasource": "prometheus", + "definition": "metrics(.+)", + "description": null, + "error": null, + "hide": 0, + "includeAll": false, + "label": "Method", + "multi": false, + "name": "method", + "options": [], + "query": { + "query": "metrics(.+)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/device_driver_(.+config)_histogram_duration_bucket/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": { + "selected": false, + "text": "emulated", + "value": "emulated" + }, + "datasource": "prometheus", + "definition": "label_values(device_driver_[[method]]_histogram_duration_bucket, driver)", + "description": null, + "error": null, + "hide": 0, + "includeAll": false, + "label": "Driver", + "multi": false, + "name": "driver", + "options": [], + "query": { + "query": "label_values(device_driver_[[method]]_histogram_duration_bucket, driver)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "TFS / Device / Driver", + "uid": "eAg-wsOVk", + "version": 15 +} \ No newline at end of file diff --git a/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json b/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json new file mode 100644 index 000000000..d7e55e271 --- /dev/null +++ b/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json @@ -0,0 +1,394 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 26, + "iteration": 1669829507595, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "prometheus", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.4", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "service_handler_[[method]]_counter_requests_started_total{handler=\"[[handler]]\"}", + "interval": "", + "legendFormat": "{{__name__}}", + "queryType": "randomWalk", + "refId": "A" + }, + { + "exemplar": true, + "expr": "service_handler_[[method]]_counter_requests_completed_total{handler=\"[[handler]]\"}", + "hide": false, + "interval": "", + "legendFormat": "{{__name__}}", + "refId": "B" + }, + { + "exemplar": true, + "expr": "service_handler_[[method]]_counter_requests_failed_total{handler=\"[[handler]]\"}", + "hide": false, + "interval": "", + "legendFormat": "{{__name__}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transformations": [ + { + "id": "renameByRegex", + "options": { + "regex": "service_.*_counter_requests_(.*)_total", + "renamePattern": "$1" + } + } + ], + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:935", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:936", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "prometheus", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 6 + }, + "hiddenSeries": false, + "id": 5, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.4", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "service_handler_[[method]]_histogram_duration_sum{handler=\"[[handler]]\"}", + "hide": false, + "interval": "", + "legendFormat": "total time", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Total Exec Time", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transformations": [], + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:407", + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:408", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "cards": { + "cardPadding": null, + "cardRound": null + }, + "color": { + "cardColor": "#b4ff00", + "colorScale": "sqrt", + "colorScheme": "interpolateRdYlGn", + "exponent": 0.5, + "max": null, + "min": 0, + "mode": "opacity" + }, + "dataFormat": "tsbuckets", + "datasource": "prometheus", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 12 + }, + "heatmap": {}, + "hideZeroBuckets": true, + "highlightCards": true, + "id": 2, + "legend": { + "show": false + }, + "pluginVersion": "7.5.4", + "reverseYBuckets": false, + "targets": [ + { + "exemplar": true, + "expr": "sum(increase(service_handler_[[method]]_histogram_duration_bucket{handler=\"[[handler]]\"}[$__rate_interval])) by (le)", + "format": "heatmap", + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{le}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Histogram", + "tooltip": { + "show": true, + "showHistogram": true + }, + "type": "heatmap", + "xAxis": { + "show": true + }, + "xBucketNumber": null, + "xBucketSize": null, + "yAxis": { + "decimals": null, + "format": "s", + "logBase": 1, + "max": null, + "min": null, + "show": true, + "splitFactor": null + }, + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null + } + ], + "refresh": false, + "schemaVersion": 27, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": { + "selected": true, + "text": "setconfig", + "value": "setconfig" + }, + "datasource": "prometheus", + "definition": "metrics(.+)", + "description": null, + "error": null, + "hide": 0, + "includeAll": false, + "label": "Method", + "multi": false, + "name": "method", + "options": [], + "query": { + "query": "metrics(.+)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/service_handler_(.+config)_histogram_duration_bucket/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": { + "selected": false, + "text": "l2nm_emulated", + "value": "l2nm_emulated" + }, + "datasource": "prometheus", + "definition": "label_values(service_handler_[[method]]_histogram_duration_bucket, handler)", + "description": null, + "error": null, + "hide": 0, + "includeAll": false, + "label": "Handler", + "multi": false, + "name": "handler", + "options": [], + "query": { + "query": "label_values(service_handler_[[method]]_histogram_duration_bucket, handler)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "TFS / Service / Handler", + "uid": "DNOhOIF4k", + "version": 6 +} \ No newline at end of file diff --git a/src/common/perf_eval_method_wrapper/tests/docker_grafana.sh b/src/common/method_wrappers/tests/old/docker_grafana.sh similarity index 100% rename from src/common/perf_eval_method_wrapper/tests/docker_grafana.sh rename to src/common/method_wrappers/tests/old/docker_grafana.sh diff --git a/src/common/perf_eval_method_wrapper/tests/prometheus.yml b/src/common/method_wrappers/tests/old/prometheus.yml similarity index 100% rename from src/common/perf_eval_method_wrapper/tests/prometheus.yml rename to src/common/method_wrappers/tests/old/prometheus.yml diff --git a/src/common/method_wrappers/tests/prometheus_queries.txt b/src/common/method_wrappers/tests/prometheus_queries.txt new file mode 100644 index 000000000..80f369934 --- /dev/null +++ b/src/common/method_wrappers/tests/prometheus_queries.txt @@ -0,0 +1,31 @@ + +variables: +name=method + query=metrics(.+) + regex=/device_driver_(.+config)_histogram_duration_bucket/ +name=driver + query=label_values(device_driver_[[method]]_histogram_duration_bucket, driver) + regex= + +plots: +device_driver_[[method]]_counter_requests_started_total{driver="[[driver]]"} +device_driver_[[method]]_counter_requests_completed_total{driver="[[driver]]"} +device_driver_[[method]]_counter_requests_failed_total{driver="[[driver]]"} +device_driver_[[method]]_histogram_duration_sum{driver="[[driver]]"} +sum(increase(device_driver_[[method]]_histogram_duration_bucket{driver="[[driver]]"}[$__rate_interval])) by (le) + + +variables: +name=method + query=metrics(.+) + regex=/service_handler_(.+config)_histogram_duration_bucket/ +name=driver + query=label_values(service_handler_[[method]]_histogram_duration_bucket, handler) + regex= + +plots: +service_handler_[[method]]_counter_requests_started_total{handler="[[handler]]"} +service_handler_[[method]]_counter_requests_completed_total{handler="[[handler]]"} +service_handler_[[method]]_counter_requests_failed_total{handler="[[handler]]"} +service_handler_[[method]]_histogram_duration_sum{handler="[[handler]]"} +sum(increase(service_handler_[[method]]_histogram_duration_bucket{handler="[[handler]]"}[$__rate_interval])) by (le) diff --git a/src/common/method_wrappers/tests/servicemonitors.yaml b/src/common/method_wrappers/tests/servicemonitors.yaml new file mode 100644 index 000000000..492e1ac60 --- /dev/null +++ b/src/common/method_wrappers/tests/servicemonitors.yaml @@ -0,0 +1,231 @@ +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + namespace: monitoring # namespace where prometheus is running + name: tfs-contextservice-metric + labels: + app: contextservice + #release: prometheus + #release: prom # name of the release + # ( VERY IMPORTANT: You need to know the correct release name by viewing + # the servicemonitor of Prometheus itself: Without the correct name, + # Prometheus cannot identify the metrics of the Flask app as the target.) +spec: + selector: + matchLabels: + # Target app service + #namespace: tfs + app: contextservice # same as above + #release: prometheus # same as above + endpoints: + - port: metrics # named port in target app + scheme: http + path: /metrics # path to scrape + interval: 1s # scrape interval + namespaceSelector: + any: false + matchNames: + - tfs # namespace where the app is running +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + namespace: monitoring # namespace where prometheus is running + name: tfs-deviceservice-metric + labels: + app: deviceservice + #release: prometheus + #release: prom # name of the release + # ( VERY IMPORTANT: You need to know the correct release name by viewing + # the servicemonitor of Prometheus itself: Without the correct name, + # Prometheus cannot identify the metrics of the Flask app as the target.) +spec: + selector: + matchLabels: + # Target app service + #namespace: tfs + app: deviceservice # same as above + #release: prometheus # same as above + endpoints: + - port: metrics # named port in target app + scheme: http + path: /metrics # path to scrape + interval: 1s # scrape interval + namespaceSelector: + any: false + matchNames: + - tfs # namespace where the app is running +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + namespace: monitoring # namespace where prometheus is running + name: tfs-serviceservice-metric + labels: + app: serviceservice + #release: prometheus + #release: prom # name of the release + # ( VERY IMPORTANT: You need to know the correct release name by viewing + # the servicemonitor of Prometheus itself: Without the correct name, + # Prometheus cannot identify the metrics of the Flask app as the target.) +spec: + selector: + matchLabels: + # Target app service + #namespace: tfs + app: serviceservice # same as above + #release: prometheus # same as above + endpoints: + - port: metrics # named port in target app + scheme: http + path: /metrics # path to scrape + interval: 1s # scrape interval + namespaceSelector: + any: false + matchNames: + - tfs # namespace where the app is running +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + namespace: monitoring # namespace where prometheus is running + name: tfs-sliceservice-metric + labels: + app: sliceservice + #release: prometheus + #release: prom # name of the release + # ( VERY IMPORTANT: You need to know the correct release name by viewing + # the servicemonitor of Prometheus itself: Without the correct name, + # Prometheus cannot identify the metrics of the Flask app as the target.) +spec: + selector: + matchLabels: + # Target app service + #namespace: tfs + app: sliceservice # same as above + #release: prometheus # same as above + endpoints: + - port: metrics # named port in target app + scheme: http + path: /metrics # path to scrape + interval: 1s # scrape interval + namespaceSelector: + any: false + matchNames: + - tfs # namespace where the app is running +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + namespace: monitoring # namespace where prometheus is running + name: tfs-pathcompservice-metric + labels: + app: pathcompservice + #release: prometheus + #release: prom # name of the release + # ( VERY IMPORTANT: You need to know the correct release name by viewing + # the servicemonitor of Prometheus itself: Without the correct name, + # Prometheus cannot identify the metrics of the Flask app as the target.) +spec: + selector: + matchLabels: + # Target app service + #namespace: tfs + app: pathcompservice # same as above + #release: prometheus # same as above + endpoints: + - port: metrics # named port in target app + scheme: http + path: /metrics # path to scrape + interval: 1s # scrape interval + namespaceSelector: + any: false + matchNames: + - tfs # namespace where the app is running +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + namespace: monitoring # namespace where prometheus is running + name: tfs-monitoringservice-metric + labels: + app: monitoringservice + #release: prometheus + #release: prom # name of the release + # ( VERY IMPORTANT: You need to know the correct release name by viewing + # the servicemonitor of Prometheus itself: Without the correct name, + # Prometheus cannot identify the metrics of the Flask app as the target.) +spec: + selector: + matchLabels: + # Target app service + #namespace: tfs + app: monitoringservice # same as above + #release: prometheus # same as above + endpoints: + - port: metrics # named port in target app + scheme: http + path: /metrics # path to scrape + interval: 1s # scrape interval + namespaceSelector: + any: false + matchNames: + - tfs # namespace where the app is running +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + namespace: monitoring # namespace where prometheus is running + name: tfs-dltservice-metric + labels: + app: dltservice + #release: prometheus + #release: prom # name of the release + # ( VERY IMPORTANT: You need to know the correct release name by viewing + # the servicemonitor of Prometheus itself: Without the correct name, + # Prometheus cannot identify the metrics of the Flask app as the target.) +spec: + selector: + matchLabels: + # Target app service + #namespace: tfs + app: dltservice # same as above + #release: prometheus # same as above + endpoints: + - port: metrics # named port in target app + scheme: http + path: /metrics # path to scrape + interval: 1s # scrape interval + namespaceSelector: + any: false + matchNames: + - tfs # namespace where the app is running +--- +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + namespace: monitoring # namespace where prometheus is running + name: tfs-interdomainservice-metric + labels: + app: interdomainservice + #release: prometheus + #release: prom # name of the release + # ( VERY IMPORTANT: You need to know the correct release name by viewing + # the servicemonitor of Prometheus itself: Without the correct name, + # Prometheus cannot identify the metrics of the Flask app as the target.) +spec: + selector: + matchLabels: + # Target app service + #namespace: tfs + app: interdomainservice # same as above + #release: prometheus # same as above + endpoints: + - port: metrics # named port in target app + scheme: http + path: /metrics # path to scrape + interval: 1s # scrape interval + namespaceSelector: + any: false + matchNames: + - tfs # namespace where the app is running diff --git a/src/common/rpc_method_wrapper/tests/test_unitary.py b/src/common/method_wrappers/tests/test_unitary.py similarity index 50% rename from src/common/rpc_method_wrapper/tests/test_unitary.py rename to src/common/method_wrappers/tests/test_unitary.py index c8fc7a2aa..95e40f641 100644 --- a/src/common/rpc_method_wrapper/tests/test_unitary.py +++ b/src/common/method_wrappers/tests/test_unitary.py @@ -13,24 +13,15 @@ # limitations under the License. import grpc, logging, time -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method logging.basicConfig(level=logging.DEBUG) LOGGER = logging.getLogger(__name__) +METRICS_POOL = MetricsPool('Context', 'RPC') def test_database_instantiation(): - SERVICE_NAME = 'Context' - METHOD_NAMES = [ - 'ListContextIds', 'ListContexts', 'GetContext', 'SetContext', 'RemoveContext', 'GetContextEvents', - 'ListTopologyIds', 'ListTopologies', 'GetTopology', 'SetTopology', 'RemoveTopology', 'GetTopologyEvents', - 'ListDeviceIds', 'ListDevices', 'GetDevice', 'SetDevice', 'RemoveDevice', 'GetDeviceEvents', - 'ListLinkIds', 'ListLinks', 'GetLink', 'SetLink', 'RemoveLink', 'GetLinkEvents', - 'ListServiceIds', 'ListServices', 'GetService', 'SetService', 'RemoveService', 'GetServiceEvents', - ] - METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) - class TestServiceServicerImpl: - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetTopology(self, request, grpc_context : grpc.ServicerContext): print('doing funny things') time.sleep(0.1) @@ -39,6 +30,7 @@ def test_database_instantiation(): tssi = TestServiceServicerImpl() tssi.GetTopology(1, 2) - for metric_name,metric in METRICS.items(): - if 'GETTOPOLOGY_' not in metric_name: continue - print(metric_name, metric._child_samples()) # pylint: disable=protected-access + for metric_name,metric in METRICS_POOL.metrics.items(): + if 'TFS_CONTEXT_RPC_GETTOPOLOGY_' not in metric_name: continue + print(metric_name, metric._child_samples()) + raise Exception() diff --git a/src/common/orm/HighLevel.py b/src/common/orm/HighLevel.py index a5bdeae3e..3a4b0d661 100644 --- a/src/common/orm/HighLevel.py +++ b/src/common/orm/HighLevel.py @@ -13,7 +13,7 @@ # limitations under the License. from typing import Any, Dict, List, Optional, Set, Tuple -from common.rpc_method_wrapper.ServiceExceptions import NotFoundException +from common.method_wrappers.ServiceExceptions import NotFoundException from common.orm.Database import Database from common.orm.backend.Tools import key_to_str from common.orm.fields.ForeignKeyField import ForeignKeyField diff --git a/src/common/perf_eval_method_wrapper/Decorator.py b/src/common/perf_eval_method_wrapper/Decorator.py deleted file mode 100644 index 6cd3a0cef..000000000 --- a/src/common/perf_eval_method_wrapper/Decorator.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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 threading -from enum import Enum -from typing import Dict, Tuple -from prometheus_client import Counter, Histogram -from prometheus_client.metrics import MetricWrapperBase, INF - -class MetricTypeEnum(Enum): - COUNTER_STARTED = '{:s}_counter_requests_started' - COUNTER_COMPLETED = '{:s}_counter_requests_completed' - COUNTER_FAILED = '{:s}_counter_requests_failed' - HISTOGRAM_DURATION = '{:s}_histogram_duration' - -METRIC_TO_CLASS_PARAMS = { - MetricTypeEnum.COUNTER_STARTED : (Counter, {}), - MetricTypeEnum.COUNTER_COMPLETED : (Counter, {}), - MetricTypeEnum.COUNTER_FAILED : (Counter, {}), - MetricTypeEnum.HISTOGRAM_DURATION: (Histogram, { - 'buckets': (.005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, INF) - }) -} - -class MetricsPool: - def __init__(self, labels : Dict[str, str] = {}) -> None: - self._labels = labels - self._metrics : Dict[str, MetricWrapperBase] = dict() - self._lock = threading.Lock() - - def get_labels(self) -> Dict[str, str]: return self._labels - - def get_or_create(self, function_name : str, metric_type : MetricTypeEnum, **metric_params) -> MetricWrapperBase: - metric_name = str(metric_type.value).format(function_name).upper() - with self._lock: - if metric_name not in self._metrics: - metric_tuple : Tuple[MetricWrapperBase, Dict] = METRIC_TO_CLASS_PARAMS.get(metric_type) - metric_class, default_metric_params = metric_tuple - if len(metric_params) == 0: metric_params = default_metric_params - labels = sorted(self._labels.keys()) - self._metrics[metric_name] = metric_class(metric_name, '', labels, **metric_params) - return self._metrics[metric_name] - -def meter_method(metrics_pool : MetricsPool): - def outer_wrapper(func): - func_name = func.__name__ - labels = metrics_pool.get_labels() - histogram_duration : Histogram = metrics_pool.get_or_create(func_name, MetricTypeEnum.HISTOGRAM_DURATION) - counter_started : Counter = metrics_pool.get_or_create(func_name, MetricTypeEnum.COUNTER_STARTED) - counter_completed : Counter = metrics_pool.get_or_create(func_name, MetricTypeEnum.COUNTER_COMPLETED) - counter_failed : Counter = metrics_pool.get_or_create(func_name, MetricTypeEnum.COUNTER_FAILED) - - @histogram_duration.labels(**labels).time() - def inner_wrapper(self, *args, **kwargs): - counter_started.labels(**labels).inc() - try: - reply = func(self, *args, **kwargs) - counter_completed.labels(**labels).inc() - return reply - except KeyboardInterrupt: # pylint: disable=try-except-raise - raise - except Exception: # pylint: disable=broad-except - counter_failed.labels(**labels).inc() - - return inner_wrapper - return outer_wrapper diff --git a/src/common/perf_eval_method_wrapper/tests/dashboard_prometheus_histogram.json b/src/common/perf_eval_method_wrapper/tests/dashboard_prometheus_histogram.json deleted file mode 100644 index 21aa91146..000000000 --- a/src/common/perf_eval_method_wrapper/tests/dashboard_prometheus_histogram.json +++ /dev/null @@ -1,110 +0,0 @@ -{ - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": null, - "graphTooltip": 0, - "id": 25, - "links": [], - "panels": [ - { - "cards": { - "cardPadding": null, - "cardRound": null - }, - "color": { - "cardColor": "#b4ff00", - "colorScale": "sqrt", - "colorScheme": "interpolateRdYlGn", - "exponent": 0.5, - "max": null, - "min": 0, - "mode": "opacity" - }, - "dataFormat": "tsbuckets", - "datasource": "prometheus", - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "gridPos": { - "h": 11, - "w": 24, - "x": 0, - "y": 0 - }, - "heatmap": {}, - "hideZeroBuckets": false, - "highlightCards": true, - "id": 2, - "legend": { - "show": true - }, - "pluginVersion": "7.5.4", - "reverseYBuckets": false, - "targets": [ - { - "exemplar": true, - "expr": "sum(increase(Context_GetDevice_histogram_duration_bucket[$__interval])) by (le)", - "format": "heatmap", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{le}}", - "queryType": "randomWalk", - "refId": "A" - } - ], - "title": "TFS / Histogram Component RPCs", - "tooltip": { - "show": true, - "showHistogram": true - }, - "type": "heatmap", - "xAxis": { - "show": true - }, - "xBucketNumber": null, - "xBucketSize": null, - "yAxis": { - "decimals": null, - "format": "s", - "logBase": 1, - "max": null, - "min": null, - "show": true, - "splitFactor": null - }, - "yBucketBound": "auto", - "yBucketNumber": null, - "yBucketSize": null - } - ], - "refresh": "5s", - "schemaVersion": 27, - "style": "dark", - "tags": [], - "templating": { - "list": [] - }, - "time": { - "from": "now-30m", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "TFS / Histogram Component RPCs", - "uid": "eAg-wsOVk", - "version": 2 -} diff --git a/src/common/perf_eval_method_wrapper/tests/servicemonitors.yaml b/src/common/perf_eval_method_wrapper/tests/servicemonitors.yaml deleted file mode 100644 index 403657101..000000000 --- a/src/common/perf_eval_method_wrapper/tests/servicemonitors.yaml +++ /dev/null @@ -1,115 +0,0 @@ -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - namespace: monitoring # namespace where prometheus is running - name: tfs-contextservice-metric - labels: - app: contextservice - #release: prometheus - #release: prom # name of the release - # ( VERY IMPORTANT: You need to know the correct release name by viewing - # the servicemonitor of Prometheus itself: Without the correct name, - # Prometheus cannot identify the metrics of the Flask app as the target.) -spec: - selector: - matchLabels: - # Target app service - #namespace: tfs - app: contextservice # same as above - #release: prometheus # same as above - endpoints: - - port: metrics # named port in target app - scheme: http - path: /metrics # path to scrape - interval: 15s # scrape interval - namespaceSelector: - any: false - matchNames: - - tfs # namespace where the app is running ---- -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - namespace: monitoring # namespace where prometheus is running - name: tfs-deviceservice-metric - labels: - app: deviceservice - #release: prometheus - #release: prom # name of the release - # ( VERY IMPORTANT: You need to know the correct release name by viewing - # the servicemonitor of Prometheus itself: Without the correct name, - # Prometheus cannot identify the metrics of the Flask app as the target.) -spec: - selector: - matchLabels: - # Target app service - #namespace: tfs - app: deviceservice # same as above - #release: prometheus # same as above - endpoints: - - port: metrics # named port in target app - scheme: http - path: /metrics # path to scrape - interval: 15s # scrape interval - namespaceSelector: - any: false - matchNames: - - tfs # namespace where the app is running ---- -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - namespace: monitoring # namespace where prometheus is running - name: tfs-serviceservice-metric - labels: - app: serviceservice - #release: prometheus - #release: prom # name of the release - # ( VERY IMPORTANT: You need to know the correct release name by viewing - # the servicemonitor of Prometheus itself: Without the correct name, - # Prometheus cannot identify the metrics of the Flask app as the target.) -spec: - selector: - matchLabels: - # Target app service - #namespace: tfs - app: serviceservice # same as above - #release: prometheus # same as above - endpoints: - - port: metrics # named port in target app - scheme: http - path: /metrics # path to scrape - interval: 15s # scrape interval - namespaceSelector: - any: false - matchNames: - - tfs # namespace where the app is running ---- -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - namespace: monitoring # namespace where prometheus is running - name: tfs-sliceservice-metric - labels: - app: sliceservice - #release: prometheus - #release: prom # name of the release - # ( VERY IMPORTANT: You need to know the correct release name by viewing - # the servicemonitor of Prometheus itself: Without the correct name, - # Prometheus cannot identify the metrics of the Flask app as the target.) -spec: - selector: - matchLabels: - # Target app service - #namespace: tfs - app: sliceservice # same as above - #release: prometheus # same as above - endpoints: - - port: metrics # named port in target app - scheme: http - path: /metrics # path to scrape - interval: 15s # scrape interval - namespaceSelector: - any: false - matchNames: - - tfs # namespace where the app is running diff --git a/src/common/rpc_method_wrapper/Decorator.py b/src/common/rpc_method_wrapper/Decorator.py deleted file mode 100644 index 31dc4b82b..000000000 --- a/src/common/rpc_method_wrapper/Decorator.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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 grpc, logging -from enum import Enum -from typing import Dict, List -from prometheus_client import Counter, Histogram -from prometheus_client.metrics import MetricWrapperBase -from common.tools.grpc.Tools import grpc_message_to_json_string -from .ServiceExceptions import ServiceException - -class RequestConditionEnum(Enum): - STARTED = 'started' - COMPLETED = 'completed' - FAILED = 'failed' - -def get_counter_requests(method_name : str, request_condition : RequestConditionEnum) -> Counter: - str_request_condition = request_condition.value - name = '{:s}_counter_requests_{:s}'.format(method_name.replace(':', '_'), str_request_condition) - description = '{:s} counter of requests {:s}'.format(method_name, str_request_condition) - return Counter(name, description) - -def get_histogram_duration(method_name : str) -> Histogram: - name = '{:s}_histogram_duration'.format(method_name.replace(':', '_')) - description = '{:s} histogram of request duration'.format(method_name) - return Histogram(name, description) - -METRIC_TEMPLATES = { - '{:s}_COUNTER_STARTED' : lambda method_name: get_counter_requests (method_name, RequestConditionEnum.STARTED), - '{:s}_COUNTER_COMPLETED' : lambda method_name: get_counter_requests (method_name, RequestConditionEnum.COMPLETED), - '{:s}_COUNTER_FAILED' : lambda method_name: get_counter_requests (method_name, RequestConditionEnum.FAILED), - '{:s}_HISTOGRAM_DURATION': lambda method_name: get_histogram_duration(method_name), -} - -def create_metrics(service_name : str, method_names : List[str]) -> Dict[str, MetricWrapperBase]: - metrics = {} - for method_name in method_names: - for template_name, template_generator_function in METRIC_TEMPLATES.items(): - metric_name = template_name.format(method_name).upper() - metrics[metric_name] = template_generator_function('{:s}:{:s}'.format(service_name, method_name)) - return metrics - -def safe_and_metered_rpc_method(metrics : Dict[str, MetricWrapperBase], logger : logging.Logger): - def outer_wrapper(func): - function_name = func.__name__ - HISTOGRAM_DURATION : Histogram = metrics.get('{:s}_HISTOGRAM_DURATION'.format(function_name).upper()) - COUNTER_STARTED : Counter = metrics.get('{:s}_COUNTER_STARTED' .format(function_name).upper()) - COUNTER_COMPLETED : Counter = metrics.get('{:s}_COUNTER_COMPLETED' .format(function_name).upper()) - COUNTER_FAILED : Counter = metrics.get('{:s}_COUNTER_FAILED' .format(function_name).upper()) - - @HISTOGRAM_DURATION.time() - def inner_wrapper(self, request, grpc_context : grpc.ServicerContext): - COUNTER_STARTED.inc() - try: - logger.debug('{:s} request: {:s}'.format(function_name, grpc_message_to_json_string(request))) - reply = func(self, request, grpc_context) - logger.debug('{:s} reply: {:s}'.format(function_name, grpc_message_to_json_string(reply))) - COUNTER_COMPLETED.inc() - return reply - except ServiceException as e: # pragma: no cover (ServiceException not thrown) - if e.code not in [grpc.StatusCode.NOT_FOUND, grpc.StatusCode.ALREADY_EXISTS]: - # Assume not found or already exists is just a condition, not an error - logger.exception('{:s} exception'.format(function_name)) - COUNTER_FAILED.inc() - grpc_context.abort(e.code, e.details) - except Exception as e: # pragma: no cover, pylint: disable=broad-except - logger.exception('{:s} exception'.format(function_name)) - COUNTER_FAILED.inc() - grpc_context.abort(grpc.StatusCode.INTERNAL, str(e)) - return inner_wrapper - return outer_wrapper diff --git a/src/common/rpc_method_wrapper/__init__.py b/src/common/rpc_method_wrapper/__init__.py deleted file mode 100644 index 70a332512..000000000 --- a/src/common/rpc_method_wrapper/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/common/rpc_method_wrapper/tests/__init__.py b/src/common/rpc_method_wrapper/tests/__init__.py deleted file mode 100644 index 70a332512..000000000 --- a/src/common/rpc_method_wrapper/tests/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/compute/service/ComputeServiceServicerImpl.py b/src/compute/service/ComputeServiceServicerImpl.py index f8ffd912f..a47a1db6c 100644 --- a/src/compute/service/ComputeServiceServicerImpl.py +++ b/src/compute/service/ComputeServiceServicerImpl.py @@ -13,56 +13,51 @@ # limitations under the License. import grpc, logging +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.proto.context_pb2 import ( AuthenticationResult, Empty, Service, ServiceId, ServiceIdList, ServiceStatus, TeraFlowController) from common.proto.compute_pb2_grpc import ComputeServiceServicer -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method LOGGER = logging.getLogger(__name__) -SERVICE_NAME = 'Compute' -METHOD_NAMES = [ - 'CheckCredentials', 'GetConnectivityServiceStatus', 'CreateConnectivityService', 'EditConnectivityService', - 'DeleteConnectivityService', 'GetAllActiveConnectivityServices', 'ClearAllConnectivityServices' -] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) +METRICS_POOL = MetricsPool('Compute', 'RPC') class ComputeServiceServicerImpl(ComputeServiceServicer): def __init__(self): LOGGER.info('Creating Servicer...') LOGGER.info('Servicer Created') - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def CheckCredentials(self, request : TeraFlowController, context : grpc.ServicerContext) -> AuthenticationResult: LOGGER.warning('NOT IMPLEMENTED') return AuthenticationResult() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetConnectivityServiceStatus(self, request : ServiceId, context : grpc.ServicerContext) -> ServiceStatus: LOGGER.warning('NOT IMPLEMENTED') return ServiceStatus() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def CreateConnectivityService(self, request : Service, context : grpc.ServicerContext) -> ServiceId: LOGGER.warning('NOT IMPLEMENTED') return ServiceId() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def EditConnectivityService(self, request : Service, context : grpc.ServicerContext) -> ServiceId: LOGGER.warning('NOT IMPLEMENTED') return ServiceId() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def DeleteConnectivityService(self, request : Service, context : grpc.ServicerContext) -> Empty: LOGGER.warning('NOT IMPLEMENTED') return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetAllActiveConnectivityServices(self, request : Empty, context : grpc.ServicerContext) -> ServiceIdList: LOGGER.warning('NOT IMPLEMENTED') return ServiceIdList() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ClearAllConnectivityServices(self, request : Empty, context : grpc.ServicerContext) -> Empty: LOGGER.warning('NOT IMPLEMENTED') return Empty() diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py index f8dd18819..b7130c700 100644 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py @@ -15,6 +15,8 @@ import grpc, json, logging, operator, threading from typing import Iterator, List, Set, Tuple from common.message_broker.MessageBroker import MessageBroker +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method +from common.method_wrappers.ServiceExceptions import InvalidArgumentException from common.orm.Database import Database from common.orm.HighLevel import ( get_all_objects, get_object, get_or_create_object, get_related_objects, update_or_create_object) @@ -31,8 +33,6 @@ from common.proto.context_pb2 import ( from common.proto.policy_pb2 import (PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule) from common.proto.context_pb2_grpc import ContextServiceServicer from common.proto.context_policy_pb2_grpc import ContextPolicyServiceServicer -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method -from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException from common.tools.grpc.Tools import grpc_message_to_json from context.service.database.ConfigModel import update_config from context.service.database.ConnectionModel import ConnectionModel, set_path @@ -56,19 +56,7 @@ from .Constants import ( LOGGER = logging.getLogger(__name__) -SERVICE_NAME = 'Context' -METHOD_NAMES = [ - 'ListConnectionIds', 'ListConnections', 'GetConnection', 'SetConnection', 'RemoveConnection', 'GetConnectionEvents', - 'ListContextIds', 'ListContexts', 'GetContext', 'SetContext', 'RemoveContext', 'GetContextEvents', - 'ListTopologyIds', 'ListTopologies', 'GetTopology', 'SetTopology', 'RemoveTopology', 'GetTopologyEvents', - 'ListDeviceIds', 'ListDevices', 'GetDevice', 'SetDevice', 'RemoveDevice', 'GetDeviceEvents', - 'ListLinkIds', 'ListLinks', 'GetLink', 'SetLink', 'RemoveLink', 'GetLinkEvents', - 'ListServiceIds', 'ListServices', 'GetService', 'SetService', 'RemoveService', 'GetServiceEvents', - 'ListSliceIds', 'ListSlices', 'GetSlice', 'SetSlice', 'RemoveSlice', 'GetSliceEvents', - 'ListPolicyRuleIds', 'ListPolicyRules', 'GetPolicyRule', 'SetPolicyRule', 'RemovePolicyRule', - 'UnsetService', 'UnsetSlice', -] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) +METRICS_POOL = MetricsPool('Context', 'RPC') class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceServicer): def __init__(self, database : Database, messagebroker : MessageBroker): @@ -81,28 +69,28 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer # ----- Context ---------------------------------------------------------------------------------------------------- - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListContextIds(self, request: Empty, context : grpc.ServicerContext) -> ContextIdList: with self.lock: db_contexts : List[ContextModel] = get_all_objects(self.database, ContextModel) db_contexts = sorted(db_contexts, key=operator.attrgetter('pk')) return ContextIdList(context_ids=[db_context.dump_id() for db_context in db_contexts]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListContexts(self, request: Empty, context : grpc.ServicerContext) -> ContextList: with self.lock: db_contexts : List[ContextModel] = get_all_objects(self.database, ContextModel) db_contexts = sorted(db_contexts, key=operator.attrgetter('pk')) return ContextList(contexts=[db_context.dump() for db_context in db_contexts]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetContext(self, request: ContextId, context : grpc.ServicerContext) -> Context: with self.lock: context_uuid = request.context_uuid.uuid db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) return Context(**db_context.dump(include_services=True, include_topologies=True)) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def SetContext(self, request: Context, context : grpc.ServicerContext) -> ContextId: with self.lock: context_uuid = request.context_id.context_uuid.uuid @@ -140,7 +128,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': dict_context_id}) return ContextId(**dict_context_id) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RemoveContext(self, request: ContextId, context : grpc.ServicerContext) -> Empty: with self.lock: context_uuid = request.context_uuid.uuid @@ -154,7 +142,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': dict_context_id}) return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetContextEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ContextEvent]: for message in self.messagebroker.consume({TOPIC_CONTEXT}, consume_timeout=CONSUME_TIMEOUT): yield ContextEvent(**json.loads(message.content)) @@ -162,7 +150,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer # ----- Topology --------------------------------------------------------------------------------------------------- - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListTopologyIds(self, request: ContextId, context : grpc.ServicerContext) -> TopologyIdList: with self.lock: context_uuid = request.context_uuid.uuid @@ -171,7 +159,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer db_topologies = sorted(db_topologies, key=operator.attrgetter('pk')) return TopologyIdList(topology_ids=[db_topology.dump_id() for db_topology in db_topologies]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListTopologies(self, request: ContextId, context : grpc.ServicerContext) -> TopologyList: with self.lock: context_uuid = request.context_uuid.uuid @@ -180,14 +168,14 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer db_topologies = sorted(db_topologies, key=operator.attrgetter('pk')) return TopologyList(topologies=[db_topology.dump() for db_topology in db_topologies]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology: with self.lock: str_key = key_to_str([request.context_id.context_uuid.uuid, request.topology_uuid.uuid]) db_topology : TopologyModel = get_object(self.database, TopologyModel, str_key) return Topology(**db_topology.dump(include_devices=True, include_links=True)) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def SetTopology(self, request: Topology, context : grpc.ServicerContext) -> TopologyId: with self.lock: context_uuid = request.topology_id.context_id.context_uuid.uuid @@ -224,7 +212,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) return TopologyId(**dict_topology_id) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RemoveTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Empty: with self.lock: context_uuid = request.context_id.context_uuid.uuid @@ -239,7 +227,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetTopologyEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[TopologyEvent]: for message in self.messagebroker.consume({TOPIC_TOPOLOGY}, consume_timeout=CONSUME_TIMEOUT): yield TopologyEvent(**json.loads(message.content)) @@ -247,21 +235,21 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer # ----- Device ----------------------------------------------------------------------------------------------------- - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListDeviceIds(self, request: Empty, context : grpc.ServicerContext) -> DeviceIdList: with self.lock: db_devices : List[DeviceModel] = get_all_objects(self.database, DeviceModel) db_devices = sorted(db_devices, key=operator.attrgetter('pk')) return DeviceIdList(device_ids=[db_device.dump_id() for db_device in db_devices]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListDevices(self, request: Empty, context : grpc.ServicerContext) -> DeviceList: with self.lock: db_devices : List[DeviceModel] = get_all_objects(self.database, DeviceModel) db_devices = sorted(db_devices, key=operator.attrgetter('pk')) return DeviceList(devices=[db_device.dump() for db_device in db_devices]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Device: with self.lock: device_uuid = request.device_uuid.uuid @@ -269,7 +257,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer return Device(**db_device.dump( include_config_rules=True, include_drivers=True, include_endpoints=True)) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def SetDevice(self, request: Device, context : grpc.ServicerContext) -> DeviceId: with self.lock: device_uuid = request.device_id.device_uuid.uuid @@ -334,7 +322,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id}) return DeviceId(**dict_device_id) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RemoveDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Empty: with self.lock: device_uuid = request.device_uuid.uuid @@ -349,7 +337,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id}) return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetDeviceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[DeviceEvent]: for message in self.messagebroker.consume({TOPIC_DEVICE}, consume_timeout=CONSUME_TIMEOUT): yield DeviceEvent(**json.loads(message.content)) @@ -357,28 +345,28 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer # ----- Link ------------------------------------------------------------------------------------------------------- - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListLinkIds(self, request: Empty, context : grpc.ServicerContext) -> LinkIdList: with self.lock: db_links : List[LinkModel] = get_all_objects(self.database, LinkModel) db_links = sorted(db_links, key=operator.attrgetter('pk')) return LinkIdList(link_ids=[db_link.dump_id() for db_link in db_links]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListLinks(self, request: Empty, context : grpc.ServicerContext) -> LinkList: with self.lock: db_links : List[LinkModel] = get_all_objects(self.database, LinkModel) db_links = sorted(db_links, key=operator.attrgetter('pk')) return LinkList(links=[db_link.dump() for db_link in db_links]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetLink(self, request: LinkId, context : grpc.ServicerContext) -> Link: with self.lock: link_uuid = request.link_uuid.uuid db_link : LinkModel = get_object(self.database, LinkModel, link_uuid) return Link(**db_link.dump()) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def SetLink(self, request: Link, context : grpc.ServicerContext) -> LinkId: with self.lock: link_uuid = request.link_id.link_uuid.uuid @@ -423,7 +411,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_LINK, event_type, {'link_id': dict_link_id}) return LinkId(**dict_link_id) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RemoveLink(self, request: LinkId, context : grpc.ServicerContext) -> Empty: with self.lock: link_uuid = request.link_uuid.uuid @@ -438,7 +426,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_LINK, event_type, {'link_id': dict_link_id}) return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetLinkEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[LinkEvent]: for message in self.messagebroker.consume({TOPIC_LINK}, consume_timeout=CONSUME_TIMEOUT): yield LinkEvent(**json.loads(message.content)) @@ -446,7 +434,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer # ----- Service ---------------------------------------------------------------------------------------------------- - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListServiceIds(self, request: ContextId, context : grpc.ServicerContext) -> ServiceIdList: with self.lock: db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid) @@ -454,7 +442,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer db_services = sorted(db_services, key=operator.attrgetter('pk')) return ServiceIdList(service_ids=[db_service.dump_id() for db_service in db_services]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListServices(self, request: ContextId, context : grpc.ServicerContext) -> ServiceList: with self.lock: db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid) @@ -462,7 +450,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer db_services = sorted(db_services, key=operator.attrgetter('pk')) return ServiceList(services=[db_service.dump() for db_service in db_services]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetService(self, request: ServiceId, context : grpc.ServicerContext) -> Service: with self.lock: str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid]) @@ -470,7 +458,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer return Service(**db_service.dump( include_endpoint_ids=True, include_constraints=True, include_config_rules=True)) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def SetService(self, request: Service, context : grpc.ServicerContext) -> ServiceId: with self.lock: context_uuid = request.service_id.context_id.context_uuid.uuid @@ -530,7 +518,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id}) return ServiceId(**dict_service_id) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RemoveService(self, request: ServiceId, context : grpc.ServicerContext) -> Empty: with self.lock: context_uuid = request.context_id.context_uuid.uuid @@ -546,7 +534,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id}) return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetServiceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ServiceEvent]: for message in self.messagebroker.consume({TOPIC_SERVICE}, consume_timeout=CONSUME_TIMEOUT): yield ServiceEvent(**json.loads(message.content)) @@ -554,7 +542,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer # ----- Slice ---------------------------------------------------------------------------------------------------- - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListSliceIds(self, request: ContextId, context : grpc.ServicerContext) -> SliceIdList: with self.lock: db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid) @@ -562,7 +550,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer db_slices = sorted(db_slices, key=operator.attrgetter('pk')) return SliceIdList(slice_ids=[db_slice.dump_id() for db_slice in db_slices]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListSlices(self, request: ContextId, context : grpc.ServicerContext) -> SliceList: with self.lock: db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid) @@ -570,7 +558,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer db_slices = sorted(db_slices, key=operator.attrgetter('pk')) return SliceList(slices=[db_slice.dump() for db_slice in db_slices]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetSlice(self, request: SliceId, context : grpc.ServicerContext) -> Slice: with self.lock: str_key = key_to_str([request.context_id.context_uuid.uuid, request.slice_uuid.uuid]) @@ -579,7 +567,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer include_endpoint_ids=True, include_constraints=True, include_config_rules=True, include_service_ids=True, include_subslice_ids=True)) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def SetSlice(self, request: Slice, context : grpc.ServicerContext) -> SliceId: with self.lock: context_uuid = request.slice_id.context_id.context_uuid.uuid @@ -664,7 +652,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_SLICE, event_type, {'slice_id': dict_slice_id}) return SliceId(**dict_slice_id) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def UnsetSlice(self, request: Slice, context : grpc.ServicerContext) -> SliceId: with self.lock: context_uuid = request.slice_id.context_id.context_uuid.uuid @@ -713,7 +701,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_SLICE, event_type, {'slice_id': dict_slice_id}) return SliceId(**dict_slice_id) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RemoveSlice(self, request: SliceId, context : grpc.ServicerContext) -> Empty: with self.lock: context_uuid = request.context_id.context_uuid.uuid @@ -729,7 +717,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_SLICE, event_type, {'slice_id': dict_slice_id}) return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetSliceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[SliceEvent]: for message in self.messagebroker.consume({TOPIC_SLICE}, consume_timeout=CONSUME_TIMEOUT): yield SliceEvent(**json.loads(message.content)) @@ -737,7 +725,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer # ----- Connection ------------------------------------------------------------------------------------------------- - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListConnectionIds(self, request: ServiceId, context : grpc.ServicerContext) -> ConnectionIdList: with self.lock: str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid]) @@ -746,7 +734,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer db_connections = sorted(db_connections, key=operator.attrgetter('pk')) return ConnectionIdList(connection_ids=[db_connection.dump_id() for db_connection in db_connections]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListConnections(self, request: ContextId, context : grpc.ServicerContext) -> ServiceList: with self.lock: str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid]) @@ -755,13 +743,13 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer db_connections = sorted(db_connections, key=operator.attrgetter('pk')) return ConnectionList(connections=[db_connection.dump() for db_connection in db_connections]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetConnection(self, request: ConnectionId, context : grpc.ServicerContext) -> Connection: with self.lock: db_connection : ConnectionModel = get_object(self.database, ConnectionModel, request.connection_uuid.uuid) return Connection(**db_connection.dump(include_path=True, include_sub_service_ids=True)) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def SetConnection(self, request: Connection, context : grpc.ServicerContext) -> ConnectionId: with self.lock: connection_uuid = request.connection_id.connection_uuid.uuid @@ -800,7 +788,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_CONNECTION, event_type, {'connection_id': dict_connection_id}) return ConnectionId(**dict_connection_id) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RemoveConnection(self, request: ConnectionId, context : grpc.ServicerContext) -> Empty: with self.lock: db_connection = ConnectionModel(self.database, request.connection_uuid.uuid, auto_load=False) @@ -814,7 +802,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer notify_event(self.messagebroker, TOPIC_CONNECTION, event_type, {'connection_id': dict_connection_id}) return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetConnectionEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ConnectionEvent]: for message in self.messagebroker.consume({TOPIC_CONNECTION}, consume_timeout=CONSUME_TIMEOUT): yield ConnectionEvent(**json.loads(message.content)) @@ -822,28 +810,28 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer # ----- Policy ----------------------------------------------------------------------------------------------------- - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListPolicyRuleIds(self, request: Empty, context: grpc.ServicerContext) -> PolicyRuleIdList: with self.lock: db_policy_rules: List[PolicyRuleModel] = get_all_objects(self.database, PolicyRuleModel) db_policy_rules = sorted(db_policy_rules, key=operator.attrgetter('pk')) return PolicyRuleIdList(policyRuleIdList=[db_policy_rule.dump_id() for db_policy_rule in db_policy_rules]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ListPolicyRules(self, request: Empty, context: grpc.ServicerContext) -> PolicyRuleList: with self.lock: db_policy_rules: List[PolicyRuleModel] = get_all_objects(self.database, PolicyRuleModel) db_policy_rules = sorted(db_policy_rules, key=operator.attrgetter('pk')) return PolicyRuleList(policyRules=[db_policy_rule.dump() for db_policy_rule in db_policy_rules]) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetPolicyRule(self, request: PolicyRuleId, context: grpc.ServicerContext) -> PolicyRule: with self.lock: policy_rule_uuid = request.uuid.uuid db_policy_rule: PolicyRuleModel = get_object(self.database, PolicyRuleModel, policy_rule_uuid) return PolicyRule(**db_policy_rule.dump()) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def SetPolicyRule(self, request: PolicyRule, context: grpc.ServicerContext) -> PolicyRuleId: with self.lock: policy_rule_type = request.WhichOneof('policy_rule') @@ -858,7 +846,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer #notify_event(self.messagebroker, TOPIC_POLICY, event_type, {"policy_id": dict_policy_id}) return PolicyRuleId(**dict_policy_id) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RemovePolicyRule(self, request: PolicyRuleId, context: grpc.ServicerContext) -> Empty: with self.lock: policy_uuid = request.uuid.uuid diff --git a/src/dbscanserving/service/DbscanServiceServicerImpl.py b/src/dbscanserving/service/DbscanServiceServicerImpl.py index 5560eec1e..b14729d36 100644 --- a/src/dbscanserving/service/DbscanServiceServicerImpl.py +++ b/src/dbscanserving/service/DbscanServiceServicerImpl.py @@ -14,15 +14,13 @@ import os, grpc, logging from sklearn.cluster import DBSCAN -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from dbscanserving.proto.dbscanserving_pb2 import DetectionRequest, DetectionResponse from dbscanserving.proto.dbscanserving_pb2_grpc import DetectorServicer LOGGER = logging.getLogger(__name__) -SERVICE_NAME = 'DbscanServing' -METHOD_NAMES = ['Detect'] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) +METRICS_POOL = MetricsPool('DbscanServing', 'RPC') class DbscanServiceServicerImpl(DetectorServicer): @@ -31,7 +29,7 @@ class DbscanServiceServicerImpl(DetectorServicer): LOGGER.debug('Creating Servicer...') LOGGER.debug('Servicer Created') - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def Detect(self, request : DetectionRequest, context : grpc.ServicerContext) -> DetectionResponse: if request.num_samples != len(request.samples): context.set_details("The sample dimension declared does not match with the number of samples received.") diff --git a/src/device/service/DeviceServiceServicerImpl.py b/src/device/service/DeviceServiceServicerImpl.py index d5d44f34f..88f49de6f 100644 --- a/src/device/service/DeviceServiceServicerImpl.py +++ b/src/device/service/DeviceServiceServicerImpl.py @@ -14,6 +14,8 @@ import grpc, json, logging, re from typing import Any, Dict, List, Tuple +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method +from common.method_wrappers.ServiceExceptions import InvalidArgumentException, OperationFailedException from common.orm.Database import Database from common.orm.HighLevel import get_object, update_or_create_object from common.orm.backend.Tools import key_to_str @@ -21,8 +23,6 @@ from common.proto.context_pb2 import ConfigActionEnum, Device, DeviceConfig, Dev from common.proto.device_pb2 import MonitoringSettings from common.proto.device_pb2_grpc import DeviceServiceServicer from common.proto.kpi_sample_types_pb2 import KpiSampleType -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method -from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException, OperationFailedException from common.tools.grpc.Tools import grpc_message_to_json from common.tools.mutex_queues.MutexQueues import MutexQueues from context.client.ContextClient import ContextClient @@ -44,9 +44,7 @@ from .MonitoringLoops import MonitoringLoops LOGGER = logging.getLogger(__name__) -SERVICE_NAME = 'Device' -METHOD_NAMES = ['AddDevice', 'ConfigureDevice', 'DeleteDevice', 'GetInitialConfig', 'MonitorDeviceKpi'] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) +METRICS_POOL = MetricsPool('Device', 'RPC') class DeviceServiceServicerImpl(DeviceServiceServicer): def __init__( @@ -60,7 +58,7 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): self.mutex_queues = MutexQueues() LOGGER.debug('Servicer Created') - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def AddDevice(self, request : Device, context : grpc.ServicerContext) -> DeviceId: device_id = request.device_id device_uuid = device_id.device_uuid.uuid @@ -176,7 +174,7 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): finally: self.mutex_queues.signal_done(device_uuid) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ConfigureDevice(self, request : Device, context : grpc.ServicerContext) -> DeviceId: device_id = request.device_id device_uuid = device_id.device_uuid.uuid @@ -243,7 +241,7 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): self.mutex_queues.signal_done(device_uuid) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def DeleteDevice(self, request : DeviceId, context : grpc.ServicerContext) -> Empty: device_uuid = request.device_uuid.uuid @@ -288,7 +286,7 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): finally: self.mutex_queues.signal_done(device_uuid) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetInitialConfig(self, request : DeviceId, context : grpc.ServicerContext) -> DeviceConfig: device_uuid = request.device_uuid.uuid @@ -303,7 +301,7 @@ class DeviceServiceServicerImpl(DeviceServiceServicer): finally: self.mutex_queues.signal_done(device_uuid) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def MonitorDeviceKpi(self, request : MonitoringSettings, context : grpc.ServicerContext) -> Empty: kpi_uuid = request.kpi_id.kpi_id.uuid device_uuid = request.kpi_descriptor.device_id.device_uuid.uuid diff --git a/src/device/service/database/DatabaseTools.py b/src/device/service/database/DatabaseTools.py index 4409f078b..9d3b712ca 100644 --- a/src/device/service/database/DatabaseTools.py +++ b/src/device/service/database/DatabaseTools.py @@ -14,11 +14,11 @@ import grpc from typing import Any, Dict, Tuple +from common.method_wrappers.ServiceExceptions import InvalidArgumentException from common.orm.Database import Database from common.orm.HighLevel import get_or_create_object, update_or_create_object from common.orm.backend.Tools import key_to_str from common.proto.context_pb2 import Device, DeviceId -from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException from context.client.ContextClient import ContextClient from device.service.driver_api.FilterFields import FilterFieldEnum from .ConfigModel import delete_all_config_rules, grpc_config_rules_to_raw, update_config diff --git a/src/device/service/drivers/emulated/EmulatedDriver.py b/src/device/service/drivers/emulated/EmulatedDriver.py index 94da07bfa..e980bbc43 100644 --- a/src/device/service/drivers/emulated/EmulatedDriver.py +++ b/src/device/service/drivers/emulated/EmulatedDriver.py @@ -19,7 +19,7 @@ from apscheduler.executors.pool import ThreadPoolExecutor from apscheduler.job import Job from apscheduler.jobstores.memory import MemoryJobStore from apscheduler.schedulers.background import BackgroundScheduler -from common.perf_eval_method_wrapper.Decorator import MetricsPool, meter_method +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.type_checkers.Checkers import chk_float, chk_length, chk_string, chk_type from device.service.database.KpiSampleType import ORM_KpiSampleTypeEnum, grpc_to_enum__kpi_sample_type from device.service.driver_api._Driver import ( @@ -123,7 +123,7 @@ def do_sampling( value = abs(0.95 * waveform + 0.05 * noise) out_samples.put_nowait((timestamp, resource_key, value)) -METRICS_POOL = MetricsPool(labels={'driver': 'emulated'}) +METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': 'emulated'}) class EmulatedDriver(_Driver): def __init__(self, address : str, port : int, **settings) -> None: # pylint: disable=super-init-not-called @@ -173,12 +173,12 @@ class EmulatedDriver(_Driver): self.__scheduler.shutdown() return True - @meter_method(METRICS_POOL) + @metered_subclass_method(METRICS_POOL) def GetInitialConfig(self) -> List[Tuple[str, Any]]: with self.__lock: return dump_subtree(self.__initial) - @meter_method(METRICS_POOL) + @metered_subclass_method(METRICS_POOL) def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]: chk_type('resources', resource_keys, list) with self.__lock: @@ -202,7 +202,7 @@ class EmulatedDriver(_Driver): results.extend(dump_subtree(resource_node)) return results - @meter_method(METRICS_POOL) + @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] @@ -237,7 +237,7 @@ class EmulatedDriver(_Driver): results.append(True) return results - @meter_method(METRICS_POOL) + @metered_subclass_method(METRICS_POOL) def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] @@ -275,7 +275,7 @@ class EmulatedDriver(_Driver): results.append(True) return results - @meter_method(METRICS_POOL) + @metered_subclass_method(METRICS_POOL) def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: chk_type('subscriptions', subscriptions, list) if len(subscriptions) == 0: return [] @@ -313,7 +313,7 @@ class EmulatedDriver(_Driver): results.append(True) return results - @meter_method(METRICS_POOL) + @metered_subclass_method(METRICS_POOL) def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: chk_type('subscriptions', subscriptions, list) if len(subscriptions) == 0: return [] diff --git a/src/device/service/drivers/microwave/IETFApiDriver.py b/src/device/service/drivers/microwave/IETFApiDriver.py index 4d5ec439f..b2652f91b 100644 --- a/src/device/service/drivers/microwave/IETFApiDriver.py +++ b/src/device/service/drivers/microwave/IETFApiDriver.py @@ -14,6 +14,7 @@ import logging, requests, threading from typing import Any, Iterator, List, Optional, Tuple, Union +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.type_checkers.Checkers import chk_string, chk_type from device.service.driver_api._Driver import _Driver from . import ALL_RESOURCE_KEYS @@ -21,6 +22,8 @@ from .Tools import create_connectivity_service, find_key, config_getter, delete_ LOGGER = logging.getLogger(__name__) +METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': 'microwave'}) + class IETFApiDriver(_Driver): def __init__(self, address: str, port: int, **settings) -> None: # pylint: disable=super-init-not-called self.__lock = threading.Lock() @@ -50,10 +53,12 @@ class IETFApiDriver(_Driver): self.__terminate.set() return True + @metered_subclass_method(METRICS_POOL) def GetInitialConfig(self) -> List[Tuple[str, Any]]: with self.__lock: return [] + @metered_subclass_method(METRICS_POOL) def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]: chk_type('resources', resource_keys, list) results = [] @@ -65,6 +70,7 @@ class IETFApiDriver(_Driver): results.extend(config_getter(self.__ietf_root, resource_key, self.__timeout)) return results + @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: results = [] if len(resources) == 0: @@ -85,6 +91,7 @@ class IETFApiDriver(_Driver): results.extend(data) return results + @metered_subclass_method(METRICS_POOL) def DeleteConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: results = [] if len(resources) == 0: return results @@ -95,10 +102,12 @@ class IETFApiDriver(_Driver): results.extend(delete_connectivity_service(self.__ietf_root, self.__timeout, uuid)) return results + @metered_subclass_method(METRICS_POOL) def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: # TODO: IETF API Driver does not support monitoring by now return [False for _ in subscriptions] + @metered_subclass_method(METRICS_POOL) def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: # TODO: IETF API Driver does not support monitoring by now return [False for _ in subscriptions] diff --git a/src/device/service/drivers/openconfig/OpenConfigDriver.py b/src/device/service/drivers/openconfig/OpenConfigDriver.py index 9342e650b..5ecf613e2 100644 --- a/src/device/service/drivers/openconfig/OpenConfigDriver.py +++ b/src/device/service/drivers/openconfig/OpenConfigDriver.py @@ -21,6 +21,7 @@ from apscheduler.job import Job from apscheduler.jobstores.memory import MemoryJobStore from apscheduler.schedulers.background import BackgroundScheduler from ncclient.manager import Manager, connect_ssh +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.tools.client.RetryDecorator import delay_exponential from common.type_checkers.Checkers import chk_length, chk_string, chk_type, chk_float from device.service.driver_api.Exceptions import UnsupportedResourceKeyException @@ -222,6 +223,8 @@ def edit_config( results[i] = e # if validation fails, store the exception return results +METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': 'openconfig'}) + class OpenConfigDriver(_Driver): def __init__(self, address : str, port : int, **settings) -> None: # pylint: disable=super-init-not-called self.__lock = threading.Lock() @@ -260,10 +263,12 @@ class OpenConfigDriver(_Driver): self.__netconf_handler.disconnect() return True + @metered_subclass_method(METRICS_POOL) def GetInitialConfig(self) -> List[Tuple[str, Any]]: with self.__lock: return [] + @metered_subclass_method(METRICS_POOL) def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]: chk_type('resources', resource_keys, list) results = [] @@ -284,6 +289,7 @@ class OpenConfigDriver(_Driver): results.append((resource_key, e)) # if validation fails, store the exception return results + @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] @@ -303,6 +309,7 @@ class OpenConfigDriver(_Driver): results = edit_config(self.__netconf_handler, resources) return results + @metered_subclass_method(METRICS_POOL) def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] @@ -322,6 +329,7 @@ class OpenConfigDriver(_Driver): results = edit_config(self.__netconf_handler, resources, delete=True) return results + @metered_subclass_method(METRICS_POOL) def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: chk_type('subscriptions', subscriptions, list) if len(subscriptions) == 0: return [] @@ -359,6 +367,7 @@ class OpenConfigDriver(_Driver): results.append(True) return results + @metered_subclass_method(METRICS_POOL) def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: chk_type('subscriptions', subscriptions, list) if len(subscriptions) == 0: return [] diff --git a/src/device/service/drivers/p4/p4_driver.py b/src/device/service/drivers/p4/p4_driver.py index b8ff795fb..606bb91eb 100644 --- a/src/device/service/drivers/p4/p4_driver.py +++ b/src/device/service/drivers/p4/p4_driver.py @@ -21,6 +21,7 @@ import json import logging import threading from typing import Any, Iterator, List, Optional, Tuple, Union +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.type_checkers.Checkers import chk_type, chk_length, chk_string from .p4_common import matches_ipv4, matches_ipv6, valid_port,\ P4_ATTR_DEV_ID, P4_ATTR_DEV_NAME, P4_ATTR_DEV_VENDOR,\ @@ -40,6 +41,7 @@ except ImportError: LOGGER = logging.getLogger(__name__) +METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': 'p4'}) class P4Driver(_Driver): """ @@ -158,6 +160,7 @@ class P4Driver(_Driver): return True + @metered_subclass_method(METRICS_POOL) def GetInitialConfig(self) -> List[Tuple[str, Any]]: """ Retrieve the initial configuration of a P4 device. @@ -172,6 +175,7 @@ class P4Driver(_Driver): self.__endpoint) return [] + @metered_subclass_method(METRICS_POOL) def GetConfig(self, resource_keys: List[str] = [])\ -> List[Tuple[str, Union[Any, None, Exception]]]: """ @@ -199,6 +203,7 @@ class P4Driver(_Driver): with self.__lock: return self.__get_resources(resource_keys) + @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources: List[Tuple[str, Any]])\ -> List[Union[bool, Exception]]: """ @@ -222,6 +227,7 @@ class P4Driver(_Driver): with self.__lock: return self.__set_resources(resources) + @metered_subclass_method(METRICS_POOL) def DeleteConfig(self, resources: List[Tuple[str, Any]])\ -> List[Union[bool, Exception]]: """ @@ -268,6 +274,7 @@ class P4Driver(_Driver): LOGGER.warning("GetState() RPC not yet implemented by the P4 driver") return [] + @metered_subclass_method(METRICS_POOL) def SubscribeState(self, subscriptions: List[Tuple[str, float, float]])\ -> List[Union[bool, Exception]]: """ @@ -280,6 +287,7 @@ class P4Driver(_Driver): "SubscribeState() RPC not yet implemented by the P4 driver") return [False for _ in subscriptions] + @metered_subclass_method(METRICS_POOL) def UnsubscribeState(self, subscriptions: List[Tuple[str, float, float]])\ -> List[Union[bool, Exception]]: """ diff --git a/src/device/service/drivers/transport_api/TransportApiDriver.py b/src/device/service/drivers/transport_api/TransportApiDriver.py index b0ecfe32f..71d7aa336 100644 --- a/src/device/service/drivers/transport_api/TransportApiDriver.py +++ b/src/device/service/drivers/transport_api/TransportApiDriver.py @@ -14,6 +14,7 @@ import logging, requests, threading from typing import Any, Iterator, List, Optional, Tuple, Union +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.type_checkers.Checkers import chk_string, chk_type from device.service.driver_api._Driver import _Driver from . import ALL_RESOURCE_KEYS @@ -21,6 +22,8 @@ from .Tools import create_connectivity_service, find_key, config_getter, delete_ LOGGER = logging.getLogger(__name__) +METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': 'transport_api'}) + class TransportApiDriver(_Driver): def __init__(self, address: str, port: int, **settings) -> None: # pylint: disable=super-init-not-called self.__lock = threading.Lock() @@ -50,10 +53,12 @@ class TransportApiDriver(_Driver): self.__terminate.set() return True + @metered_subclass_method(METRICS_POOL) def GetInitialConfig(self) -> List[Tuple[str, Any]]: with self.__lock: return [] + @metered_subclass_method(METRICS_POOL) def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]: chk_type('resources', resource_keys, list) results = [] @@ -65,6 +70,7 @@ class TransportApiDriver(_Driver): results.extend(config_getter(self.__tapi_root, resource_key, self.__timeout)) return results + @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: results = [] if len(resources) == 0: @@ -88,6 +94,7 @@ class TransportApiDriver(_Driver): results.extend(data) return results + @metered_subclass_method(METRICS_POOL) def DeleteConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: results = [] if len(resources) == 0: return results @@ -98,10 +105,12 @@ class TransportApiDriver(_Driver): results.extend(delete_connectivity_service(self.__tapi_root, self.__timeout, uuid)) return results + @metered_subclass_method(METRICS_POOL) def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: # TODO: TAPI does not support monitoring by now return [False for _ in subscriptions] + @metered_subclass_method(METRICS_POOL) def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: # TODO: TAPI does not support monitoring by now return [False for _ in subscriptions] diff --git a/src/device/service/drivers/xr/XrDriver.py b/src/device/service/drivers/xr/XrDriver.py index 51fd29ad1..1c1ee7d86 100644 --- a/src/device/service/drivers/xr/XrDriver.py +++ b/src/device/service/drivers/xr/XrDriver.py @@ -18,6 +18,7 @@ import threading import json from typing import Any, Iterator, List, Optional, Tuple, Union import urllib3 +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.type_checkers.Checkers import chk_type from device.service.driver_api._Driver import _Driver from .cm.cm_connection import CmConnection @@ -29,6 +30,8 @@ urllib3.disable_warnings() LOGGER = logging.getLogger(__name__) +METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': 'xr'}) + class XrDriver(_Driver): def __init__(self, address: str, port: int, **settings) -> None: # pylint: disable=super-init-not-called self.__lock = threading.Lock() @@ -74,6 +77,7 @@ class XrDriver(_Driver): return [] #pylint: disable=dangerous-default-value + @metered_subclass_method(METRICS_POOL) def GetConfig(self, resource_keys : List[str] = []) -> List[Tuple[str, Union[Any, None, Exception]]]: LOGGER.info(f"GetConfig[{self}]: {resource_keys=}") chk_type('resources', resource_keys, list) @@ -89,6 +93,7 @@ class XrDriver(_Driver): else: return [] + @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: LOGGER.info(f"SetConfig[{self}]: {resources=}") # Logged config seems like: @@ -116,6 +121,7 @@ class XrDriver(_Driver): return results + @metered_subclass_method(METRICS_POOL) def DeleteConfig(self, resources: List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: LOGGER.info(f"DeleteConfig[{self}]: {resources=}") @@ -156,10 +162,12 @@ class XrDriver(_Driver): return results + @metered_subclass_method(METRICS_POOL) def SubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: # Not supported return [False for _ in subscriptions] + @metered_subclass_method(METRICS_POOL) def UnsubscribeState(self, subscriptions : List[Tuple[str, float, float]]) -> List[Union[bool, Exception]]: # Not supported return [False for _ in subscriptions] diff --git a/src/dlt/connector/service/DltConnectorServiceServicerImpl.py b/src/dlt/connector/service/DltConnectorServiceServicerImpl.py index 6c5401cb1..10a423166 100644 --- a/src/dlt/connector/service/DltConnectorServiceServicerImpl.py +++ b/src/dlt/connector/service/DltConnectorServiceServicerImpl.py @@ -13,11 +13,11 @@ # limitations under the License. import grpc, logging +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.proto.context_pb2 import DeviceId, Empty, LinkId, ServiceId, SliceId, TopologyId from common.proto.dlt_connector_pb2 import DltDeviceId, DltLinkId, DltServiceId, DltSliceId from common.proto.dlt_connector_pb2_grpc import DltConnectorServiceServicer from common.proto.dlt_gateway_pb2 import DltRecord, DltRecordId, DltRecordOperationEnum, DltRecordTypeEnum -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method from common.tools.grpc.Tools import grpc_message_to_json_string from context.client.ContextClient import ContextClient from dlt.connector.client.DltGatewayClient import DltGatewayClient @@ -25,30 +25,22 @@ from .tools.Checkers import record_exists LOGGER = logging.getLogger(__name__) -SERVICE_NAME = 'DltConnector' -METHOD_NAMES = [ - 'RecordAll', - 'RecordAllDevices', 'RecordDevice', - 'RecordAllLinks', 'RecordLink', - 'RecordAllServices', 'RecordService', - 'RecordAllSlices', 'RecordSlice', -] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) +METRICS_POOL = MetricsPool('DltConnector', 'RPC') class DltConnectorServiceServicerImpl(DltConnectorServiceServicer): def __init__(self): LOGGER.debug('Creating Servicer...') LOGGER.debug('Servicer Created') - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordAll(self, request : TopologyId, context : grpc.ServicerContext) -> Empty: return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordAllDevices(self, request : TopologyId, context : grpc.ServicerContext) -> Empty: return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordDevice(self, request : DltDeviceId, context : grpc.ServicerContext) -> Empty: context_client = ContextClient() device = context_client.GetDevice(request.device_id) @@ -80,11 +72,11 @@ class DltConnectorServiceServicerImpl(DltConnectorServiceServicer): LOGGER.info('[RecordDevice] recv dlt_record_status = {:s}'.format(grpc_message_to_json_string(dlt_record_status))) return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordAllLinks(self, request : TopologyId, context : grpc.ServicerContext) -> Empty: return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordLink(self, request : DltLinkId, context : grpc.ServicerContext) -> Empty: context_client = ContextClient() link = context_client.GetLink(request.link_id) @@ -116,19 +108,19 @@ class DltConnectorServiceServicerImpl(DltConnectorServiceServicer): LOGGER.info('[RecordLink] recv dlt_record_status = {:s}'.format(grpc_message_to_json_string(dlt_record_status))) return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordAllServices(self, request : TopologyId, context : grpc.ServicerContext) -> Empty: return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordService(self, request : DltServiceId, context : grpc.ServicerContext) -> Empty: return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordAllSlices(self, request : TopologyId, context : grpc.ServicerContext) -> Empty: return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordSlice(self, request : DltSliceId, context : grpc.ServicerContext) -> Empty: context_client = ContextClient() slice_ = context_client.GetSlice(request.slice_id) diff --git a/src/interdomain/service/InterdomainServiceServicerImpl.py b/src/interdomain/service/InterdomainServiceServicerImpl.py index a178095ae..3fb3e72b9 100644 --- a/src/interdomain/service/InterdomainServiceServicerImpl.py +++ b/src/interdomain/service/InterdomainServiceServicerImpl.py @@ -16,7 +16,7 @@ import grpc, logging, uuid from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID from common.proto.context_pb2 import AuthenticationResult, Slice, SliceId, SliceStatusEnum, TeraFlowController, TopologyId from common.proto.interdomain_pb2_grpc import InterdomainServiceServicer -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.tools.context_queries.Context import create_context from common.tools.context_queries.InterDomain import ( compute_interdomain_path, compute_traversed_domains, get_local_device_uuids, is_inter_domain, is_multi_domain) @@ -33,9 +33,7 @@ from .Tools import compose_slice, compute_slice_owner, map_abstract_endpoints_to LOGGER = logging.getLogger(__name__) -SERVICE_NAME = 'Interdomain' -METHOD_NAMES = ['RequestSlice', 'Authenticate', 'LookUpSlice', 'OrderSliceFromCatalog', 'CreateSliceAndAddToCatalog'] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) +METRICS_POOL = MetricsPool('Interdomain', 'RPC') class InterdomainServiceServicerImpl(InterdomainServiceServicer): def __init__(self, remote_domain_clients : RemoteDomainClients): @@ -43,7 +41,7 @@ class InterdomainServiceServicerImpl(InterdomainServiceServicer): self.remote_domain_clients = remote_domain_clients LOGGER.debug('Servicer Created') - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RequestSlice(self, request : Slice, context : grpc.ServicerContext) -> SliceId: context_client = ContextClient() pathcomp_client = PathCompClient() @@ -133,14 +131,14 @@ class InterdomainServiceServicerImpl(InterdomainServiceServicer): slice_id = context_client.SetSlice(reply) return slice_id - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def Authenticate(self, request : TeraFlowController, context : grpc.ServicerContext) -> AuthenticationResult: auth_result = AuthenticationResult() auth_result.context_id.CopyFrom(request.context_id) # pylint: disable=no-member auth_result.authenticated = True return auth_result - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def LookUpSlice(self, request : Slice, context : grpc.ServicerContext) -> SliceId: try: context_client = ContextClient() @@ -150,12 +148,12 @@ class InterdomainServiceServicerImpl(InterdomainServiceServicer): #LOGGER.exception('Unable to get slice({:s})'.format(grpc_message_to_json_string(request.slice_id))) return SliceId() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def OrderSliceFromCatalog(self, request : Slice, context : grpc.ServicerContext) -> Slice: raise NotImplementedError('OrderSliceFromCatalog') #return Slice() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def CreateSliceAndAddToCatalog(self, request : Slice, context : grpc.ServicerContext) -> Slice: context_client = ContextClient() slice_client = SliceClient() diff --git a/src/interdomain/service/_old_code/InterdomainServiceServicerImpl.py b/src/interdomain/service/_old_code/InterdomainServiceServicerImpl.py index 01ba90ef5..f38185781 100644 --- a/src/interdomain/service/_old_code/InterdomainServiceServicerImpl.py +++ b/src/interdomain/service/_old_code/InterdomainServiceServicerImpl.py @@ -13,7 +13,7 @@ # limitations under the License. import grpc, logging -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.proto.context_pb2 import ( AuthenticationResult, Slice, SliceId, SliceStatus, SliceStatusEnum, TeraFlowController) from common.proto.interdomain_pb2_grpc import InterdomainServiceServicer @@ -24,9 +24,7 @@ from slice.client.SliceClient import SliceClient LOGGER = logging.getLogger(__name__) -SERVICE_NAME = 'Interdomain' -METHOD_NAMES = ['RequestSlice', 'Authenticate', 'LookUpSlice', 'OrderSliceFromCatalog', 'CreateSliceAndAddToCatalog'] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) +METRICS_POOL = MetricsPool('Interdomain', 'RPC') class InterdomainServiceServicerImpl(InterdomainServiceServicer): def __init__(self, remote_domain_clients : RemoteDomainClients): @@ -34,7 +32,7 @@ class InterdomainServiceServicerImpl(InterdomainServiceServicer): self.remote_domain_clients = remote_domain_clients LOGGER.debug('Servicer Created') - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RequestSlice(self, request : Slice, context : grpc.ServicerContext) -> SliceId: context_client = ContextClient() slice_client = SliceClient() @@ -121,14 +119,14 @@ class InterdomainServiceServicerImpl(InterdomainServiceServicer): context_client.SetSlice(reply) return reply.slice_id - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def Authenticate(self, request : TeraFlowController, context : grpc.ServicerContext) -> AuthenticationResult: auth_result = AuthenticationResult() auth_result.context_id.CopyFrom(request.context_id) # pylint: disable=no-member auth_result.authenticated = True return auth_result - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def LookUpSlice(self, request : Slice, context : grpc.ServicerContext) -> SliceId: try: context_client = ContextClient() @@ -138,12 +136,12 @@ class InterdomainServiceServicerImpl(InterdomainServiceServicer): #LOGGER.exception('Unable to get slice({:s})'.format(grpc_message_to_json_string(request.slice_id))) return SliceId() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def OrderSliceFromCatalog(self, request : Slice, context : grpc.ServicerContext) -> Slice: raise NotImplementedError('OrderSliceFromCatalog') #return Slice() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def CreateSliceAndAddToCatalog(self, request : Slice, context : grpc.ServicerContext) -> Slice: context_client = ContextClient() slice_client = SliceClient() diff --git a/src/monitoring/service/EventTools.py b/src/monitoring/service/EventTools.py index 4999d2a95..189e78ce6 100644 --- a/src/monitoring/service/EventTools.py +++ b/src/monitoring/service/EventTools.py @@ -17,7 +17,7 @@ from queue import Queue import grpc -from common.rpc_method_wrapper.ServiceExceptions import ServiceException +from common.method_wrappers.ServiceExceptions import ServiceException from context.client.ContextClient import ContextClient from common.proto.context_pb2 import Empty, EventTypeEnum diff --git a/src/monitoring/service/MonitoringServiceServicerImpl.py b/src/monitoring/service/MonitoringServiceServicerImpl.py index 548f34c8a..c2bceefd7 100644 --- a/src/monitoring/service/MonitoringServiceServicerImpl.py +++ b/src/monitoring/service/MonitoringServiceServicerImpl.py @@ -27,7 +27,7 @@ from common.proto.monitoring_pb2_grpc import MonitoringServiceServicer from common.proto.monitoring_pb2 import AlarmResponse, AlarmDescriptor, AlarmList, SubsList, KpiId, \ KpiDescriptor, KpiList, KpiQuery, SubsDescriptor, SubscriptionID, AlarmID, KpiDescriptorList, \ MonitorKpiRequest, Kpi, AlarmSubscription, SubsResponse, RawKpiTable, RawKpi, RawKpiList -from common.rpc_method_wrapper.ServiceExceptions import ServiceException +from common.method_wrappers.ServiceExceptions import ServiceException from common.tools.timestamp.Converters import timestamp_string_to_float, timestamp_utcnow_to_float from monitoring.service import ManagementDBTools, MetricsDBTools diff --git a/src/opticalattackmitigator/service/OpticalAttackMitigatorServiceServicerImpl.py b/src/opticalattackmitigator/service/OpticalAttackMitigatorServiceServicerImpl.py index 4a2dd041b..39a783ac4 100644 --- a/src/opticalattackmitigator/service/OpticalAttackMitigatorServiceServicerImpl.py +++ b/src/opticalattackmitigator/service/OpticalAttackMitigatorServiceServicerImpl.py @@ -14,16 +14,14 @@ import os, grpc, logging, random from influxdb import InfluxDBClient -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from opticalattackmitigator.proto.optical_attack_mitigator_pb2_grpc import ( AttackMitigatorServicer) from opticalattackmitigator.proto.optical_attack_mitigator_pb2 import AttackDescription, AttackResponse LOGGER = logging.getLogger(__name__) -SERVICE_NAME = 'OpticalAttackMitigator' -METHOD_NAMES = ['NotifyAttack'] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) +METRICS_POOL = MetricsPool('OpticalAttackMitigator', 'RPC') class OpticalAttackMitigatorServiceServicerImpl(AttackMitigatorServicer): @@ -32,7 +30,7 @@ class OpticalAttackMitigatorServiceServicerImpl(AttackMitigatorServicer): LOGGER.debug('Creating Servicer...') LOGGER.debug('Servicer Created') - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def NotifyAttack(self, request : AttackDescription, context : grpc.ServicerContext) -> AttackResponse: LOGGER.debug(f"NotifyAttack: {request}") response: AttackResponse = AttackResponse() diff --git a/src/opticalcentralizedattackdetector/service/OpticalCentralizedAttackDetectorServiceServicerImpl.py b/src/opticalcentralizedattackdetector/service/OpticalCentralizedAttackDetectorServiceServicerImpl.py index d4c71476f..0009f8d91 100644 --- a/src/opticalcentralizedattackdetector/service/OpticalCentralizedAttackDetectorServiceServicerImpl.py +++ b/src/opticalcentralizedattackdetector/service/OpticalCentralizedAttackDetectorServiceServicerImpl.py @@ -14,7 +14,7 @@ import os, grpc, logging, random from influxdb import InfluxDBClient -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from context.client.ContextClient import ContextClient from monitoring.client.MonitoringClient import MonitoringClient from service.client.ServiceClient import ServiceClient @@ -37,9 +37,7 @@ from opticalcentralizedattackdetector.Config import ( LOGGER = logging.getLogger(__name__) -SERVICE_NAME = 'OpticalCentralizedAttackDetector' -METHOD_NAMES = ['NotifyServiceUpdate', 'DetectAttack', 'ReportSummarizedKpi', 'ReportKpi'] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) +METRICS_POOL = MetricsPool('OpticalCentralizedAttackDetector', 'RPC') INFLUXDB_HOSTNAME = os.environ.get("INFLUXDB_HOSTNAME") INFLUXDB_USER = os.environ.get("INFLUXDB_USER") @@ -63,11 +61,11 @@ class OpticalCentralizedAttackDetectorServiceServicerImpl(OpticalCentralizedAtta LOGGER.debug('Creating Servicer...') LOGGER.debug('Servicer Created') - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def NotifyServiceUpdate(self, request : Service, context : grpc.ServicerContext) -> Empty: return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def DetectAttack(self, request : Empty, context : grpc.ServicerContext) -> Empty: # retrieve list with current contexts @@ -131,10 +129,10 @@ class OpticalCentralizedAttackDetectorServiceServicerImpl(OpticalCentralizedAtta # if attack is detected, run the attack mitigator return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ReportSummarizedKpi(self, request : KpiList, context : grpc.ServicerContext) -> Empty: return Empty() - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def ReportKpi(self, request : KpiList, context : grpc.ServicerContext) -> Empty: return Empty() diff --git a/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py b/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py index 205306d0e..4f725cce7 100644 --- a/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py +++ b/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py @@ -14,10 +14,10 @@ import grpc, logging from common.Constants import DEFAULT_CONTEXT_UUID, INTERDOMAIN_TOPOLOGY_UUID +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.proto.context_pb2 import ContextId, Empty from common.proto.pathcomp_pb2 import PathCompReply, PathCompRequest from common.proto.pathcomp_pb2_grpc import PathCompServiceServicer -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method from common.tools.context_queries.Device import get_devices_in_topology from common.tools.context_queries.Link import get_links_in_topology from common.tools.context_queries.InterDomain import is_inter_domain @@ -28,9 +28,7 @@ from pathcomp.frontend.service.algorithms.Factory import get_algorithm LOGGER = logging.getLogger(__name__) -SERVICE_NAME = 'PathComp' -METHOD_NAMES = ['Compute'] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) +METRICS_POOL = MetricsPool('PathComp', 'RPC') ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_UUID)) @@ -39,7 +37,7 @@ class PathCompServiceServicerImpl(PathCompServiceServicer): LOGGER.debug('Creating Servicer...') LOGGER.debug('Servicer Created') - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def Compute(self, request : PathCompRequest, context : grpc.ServicerContext) -> PathCompReply: LOGGER.info('[Compute] begin ; request = {:s}'.format(grpc_message_to_json_string(request))) diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py index 71fe14f53..6ce549666 100644 --- a/src/service/service/ServiceServiceServicerImpl.py +++ b/src/service/service/ServiceServiceServicerImpl.py @@ -14,11 +14,11 @@ import grpc, json, logging from typing import Optional +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method +from common.method_wrappers.ServiceExceptions import AlreadyExistsException, InvalidArgumentException from common.proto.context_pb2 import Empty, Service, ServiceId, ServiceStatusEnum from common.proto.pathcomp_pb2 import PathCompRequest from common.proto.service_pb2_grpc import ServiceServiceServicer -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method -from common.rpc_method_wrapper.ServiceExceptions import AlreadyExistsException, InvalidArgumentException from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string from context.client.ContextClient import ContextClient from pathcomp.frontend.client.PathCompClient import PathCompClient @@ -28,9 +28,7 @@ from .task_scheduler.TaskScheduler import TasksScheduler LOGGER = logging.getLogger(__name__) -SERVICE_NAME = 'Service' -METHOD_NAMES = ['CreateService', 'UpdateService', 'DeleteService'] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) +METRICS_POOL = MetricsPool('Service', 'RPC') class ServiceServiceServicerImpl(ServiceServiceServicer): def __init__(self, service_handler_factory : ServiceHandlerFactory) -> None: @@ -38,7 +36,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): self.service_handler_factory = service_handler_factory LOGGER.debug('Servicer Created') - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def CreateService(self, request : Service, context : grpc.ServicerContext) -> ServiceId: LOGGER.info('[CreateService] begin ; request = {:s}'.format(grpc_message_to_json_string(request))) @@ -89,7 +87,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): service_id = context_client.SetService(request) return service_id - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def UpdateService(self, request : Service, context : grpc.ServicerContext) -> ServiceId: LOGGER.info('[UpdateService] begin ; request = {:s}'.format(grpc_message_to_json_string(request))) @@ -131,7 +129,7 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): tasks_scheduler.execute_all() return request.service_id - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def DeleteService(self, request : ServiceId, context : grpc.ServicerContext) -> Empty: LOGGER.info('[DeleteService] begin ; request = {:s}'.format(grpc_message_to_json_string(request))) diff --git a/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py b/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py index 19deabda3..69e61db41 100644 --- a/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py +++ b/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py @@ -14,6 +14,7 @@ import anytree, json, logging from typing import Any, List, Optional, Tuple, Union +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service from common.tools.object_factory.Device import json_device_id from common.type_checkers.Checkers import chk_length, chk_type @@ -24,6 +25,8 @@ from .ConfigRules import setup_config_rules, teardown_config_rules LOGGER = logging.getLogger(__name__) +METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l2nm_emulated'}) + class L2NMEmulatedServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called self, service : Service, task_executor : TaskExecutor, **settings @@ -46,6 +49,7 @@ class L2NMEmulatedServiceHandler(_ServiceHandler): elif action == ConfigActionEnum.CONFIGACTION_DELETE: delete_subnode(self.__resolver, self.__config, resource_key) + @metered_subclass_method(METRICS_POOL) def SetEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: @@ -80,6 +84,7 @@ class L2NMEmulatedServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def DeleteEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: @@ -114,6 +119,7 @@ class L2NMEmulatedServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] @@ -122,6 +128,7 @@ class L2NMEmulatedServiceHandler(_ServiceHandler): LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] + @metered_subclass_method(METRICS_POOL) def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] @@ -130,6 +137,7 @@ class L2NMEmulatedServiceHandler(_ServiceHandler): LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] + @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] @@ -147,6 +155,7 @@ class L2NMEmulatedServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] diff --git a/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py b/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py index 54fb52630..dca5942ac 100644 --- a/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py +++ b/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py @@ -14,6 +14,7 @@ import anytree, json, logging from typing import Any, List, Optional, Tuple, Union +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service from common.tools.object_factory.Device import json_device_id from common.type_checkers.Checkers import chk_length, chk_type @@ -24,6 +25,8 @@ from .ConfigRules import setup_config_rules, teardown_config_rules LOGGER = logging.getLogger(__name__) +METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l3nm_emulated'}) + class L3NMEmulatedServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called self, service : Service, task_executor : TaskExecutor, **settings @@ -46,6 +49,7 @@ class L3NMEmulatedServiceHandler(_ServiceHandler): elif action == ConfigActionEnum.CONFIGACTION_DELETE: delete_subnode(self.__resolver, self.__config, resource_key) + @metered_subclass_method(METRICS_POOL) def SetEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: @@ -80,6 +84,7 @@ class L3NMEmulatedServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def DeleteEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: @@ -114,6 +119,7 @@ class L3NMEmulatedServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] @@ -122,6 +128,7 @@ class L3NMEmulatedServiceHandler(_ServiceHandler): LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] + @metered_subclass_method(METRICS_POOL) def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] @@ -130,6 +137,7 @@ class L3NMEmulatedServiceHandler(_ServiceHandler): LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] + @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] @@ -147,6 +155,7 @@ class L3NMEmulatedServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] diff --git a/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py b/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py index bdf688164..76a335745 100644 --- a/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py +++ b/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py @@ -14,6 +14,7 @@ import anytree, json, logging from typing import Any, List, Optional, Tuple, Union +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service from common.tools.object_factory.Device import json_device_id from common.type_checkers.Checkers import chk_length, chk_type @@ -24,6 +25,8 @@ from .ConfigRules import setup_config_rules, teardown_config_rules LOGGER = logging.getLogger(__name__) +METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l3nm_openconfig'}) + class L3NMOpenConfigServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called self, service : Service, task_executor : TaskExecutor, **settings @@ -46,6 +49,7 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler): elif action == ConfigActionEnum.CONFIGACTION_DELETE: delete_subnode(self.__resolver, self.__config, resource_key) + @metered_subclass_method(METRICS_POOL) def SetEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: @@ -80,6 +84,7 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def DeleteEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: @@ -114,6 +119,7 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] @@ -122,6 +128,7 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler): LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] + @metered_subclass_method(METRICS_POOL) def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] @@ -130,6 +137,7 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler): LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] + @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] @@ -147,6 +155,7 @@ class L3NMOpenConfigServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] diff --git a/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py b/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py index 1ae08bbf6..5b05160de 100644 --- a/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py +++ b/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py @@ -14,6 +14,7 @@ import anytree, json, logging from typing import Any, Dict, List, Optional, Tuple, Union +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set from common.tools.object_factory.Device import json_device_id @@ -30,6 +31,8 @@ def check_endpoint(endpoint : str, service_uuid : str) -> Tuple[str, str]: raise Exception('Endpoint({:s}) is malformed for Service({:s})'.format(str(endpoint), str(service_uuid))) return endpoint_split +METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'microwave'}) + class MicrowaveServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called self, service : Service, task_executor : TaskExecutor, **settings @@ -52,6 +55,7 @@ class MicrowaveServiceHandler(_ServiceHandler): elif action == ConfigActionEnum.CONFIGACTION_DELETE: delete_subnode(self.__resolver, self.__config, resource_key) + @metered_subclass_method(METRICS_POOL) def SetEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: @@ -95,6 +99,7 @@ class MicrowaveServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def DeleteEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: @@ -121,6 +126,7 @@ class MicrowaveServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] @@ -129,6 +135,7 @@ class MicrowaveServiceHandler(_ServiceHandler): LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] + @metered_subclass_method(METRICS_POOL) def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] @@ -137,6 +144,7 @@ class MicrowaveServiceHandler(_ServiceHandler): LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] + @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] @@ -154,6 +162,7 @@ class MicrowaveServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] diff --git a/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py b/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py index f94948129..24371203a 100644 --- a/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py +++ b/src/service/service/service_handlers/tapi_tapi/TapiServiceHandler.py @@ -14,6 +14,7 @@ import anytree, json, logging from typing import Any, Dict, List, Optional, Tuple, Union +from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service from common.tools.object_factory.ConfigRule import json_config_rule_delete, json_config_rule_set from common.tools.object_factory.Device import json_device_id @@ -24,6 +25,8 @@ from service.service.task_scheduler.TaskExecutor import TaskExecutor LOGGER = logging.getLogger(__name__) +METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'tapi_tapi'}) + class TapiServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called self, service : Service, task_executor : TaskExecutor, **settings @@ -46,6 +49,7 @@ class TapiServiceHandler(_ServiceHandler): elif action == ConfigActionEnum.CONFIGACTION_DELETE: delete_subnode(self.__resolver, self.__config, resource_key) + @metered_subclass_method(METRICS_POOL) def SetEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: @@ -89,6 +93,7 @@ class TapiServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def DeleteEndpoint( self, endpoints : List[Tuple[str, str, Optional[str]]], connection_uuid : Optional[str] = None ) -> List[Union[bool, Exception]]: @@ -114,6 +119,7 @@ class TapiServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def SetConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] @@ -122,6 +128,7 @@ class TapiServiceHandler(_ServiceHandler): LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] + @metered_subclass_method(METRICS_POOL) def DeleteConstraint(self, constraints : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('constraints', constraints, list) if len(constraints) == 0: return [] @@ -130,6 +137,7 @@ class TapiServiceHandler(_ServiceHandler): LOGGER.warning(msg.format(str(constraints))) return [True for _ in range(len(constraints))] + @metered_subclass_method(METRICS_POOL) def SetConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] @@ -147,6 +155,7 @@ class TapiServiceHandler(_ServiceHandler): return results + @metered_subclass_method(METRICS_POOL) def DeleteConfig(self, resources : List[Tuple[str, Any]]) -> List[Union[bool, Exception]]: chk_type('resources', resources, list) if len(resources) == 0: return [] diff --git a/src/service/service/task_scheduler/TaskExecutor.py b/src/service/service/task_scheduler/TaskExecutor.py index 757a66059..7c96eb665 100644 --- a/src/service/service/task_scheduler/TaskExecutor.py +++ b/src/service/service/task_scheduler/TaskExecutor.py @@ -14,8 +14,8 @@ from enum import Enum from typing import TYPE_CHECKING, Any, Dict, Optional, Union +from common.method_wrappers.ServiceExceptions import NotFoundException from common.proto.context_pb2 import Connection, ConnectionId, Device, DeviceId, Service, ServiceId -from common.rpc_method_wrapper.ServiceExceptions import NotFoundException from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from service.service.service_handler_api.ServiceHandlerFactory import ServiceHandlerFactory, get_service_handler_class diff --git a/src/service/service/task_scheduler/tasks/Task_ConnectionConfigure.py b/src/service/service/task_scheduler/tasks/Task_ConnectionConfigure.py index beb7e5a04..cfafd54e5 100644 --- a/src/service/service/task_scheduler/tasks/Task_ConnectionConfigure.py +++ b/src/service/service/task_scheduler/tasks/Task_ConnectionConfigure.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from common.method_wrappers.ServiceExceptions import OperationFailedException from common.proto.context_pb2 import ConnectionId -from common.rpc_method_wrapper.ServiceExceptions import OperationFailedException from common.tools.grpc.Tools import grpc_message_to_json_string from service.service.service_handler_api.Tools import check_errors_setendpoint from service.service.task_scheduler.TaskExecutor import TaskExecutor diff --git a/src/service/service/task_scheduler/tasks/Task_ConnectionDeconfigure.py b/src/service/service/task_scheduler/tasks/Task_ConnectionDeconfigure.py index c04d950a8..4c8b75b2f 100644 --- a/src/service/service/task_scheduler/tasks/Task_ConnectionDeconfigure.py +++ b/src/service/service/task_scheduler/tasks/Task_ConnectionDeconfigure.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from common.method_wrappers.ServiceExceptions import OperationFailedException from common.proto.context_pb2 import ConnectionId -from common.rpc_method_wrapper.ServiceExceptions import OperationFailedException from common.tools.grpc.Tools import grpc_message_to_json_string from service.service.service_handler_api.Tools import check_errors_deleteendpoint from service.service.task_scheduler.TaskExecutor import TaskExecutor diff --git a/src/slice/service/SliceServiceServicerImpl.py b/src/slice/service/SliceServiceServicerImpl.py index ada721858..aa41a77ac 100644 --- a/src/slice/service/SliceServiceServicerImpl.py +++ b/src/slice/service/SliceServiceServicerImpl.py @@ -16,7 +16,7 @@ import grpc, json, logging #, deepdiff from common.proto.context_pb2 import ( Empty, Service, ServiceId, ServiceStatusEnum, ServiceTypeEnum, Slice, SliceId, SliceStatusEnum) from common.proto.slice_pb2_grpc import SliceServiceServicer -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method +from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.tools.context_queries.InterDomain import is_multi_domain from common.tools.grpc.ConfigRules import copy_config_rules from common.tools.grpc.Constraints import copy_constraints @@ -29,9 +29,7 @@ from service.client.ServiceClient import ServiceClient LOGGER = logging.getLogger(__name__) -SERVICE_NAME = 'Slice' -METHOD_NAMES = ['CreateSlice', 'UpdateSlice', 'DeleteSlice'] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) +METRICS_POOL = MetricsPool('Slice', 'RPC') class SliceServiceServicerImpl(SliceServiceServicer): def __init__(self): @@ -158,7 +156,7 @@ class SliceServiceServicerImpl(SliceServiceServicer): context_client.SetSlice(slice_active) return slice_id - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def CreateSlice(self, request : Slice, context : grpc.ServicerContext) -> SliceId: #try: # slice_ = context_client.GetSlice(request.slice_id) @@ -168,7 +166,7 @@ class SliceServiceServicerImpl(SliceServiceServicer): #return slice_id return self.create_update(request) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def UpdateSlice(self, request : Slice, context : grpc.ServicerContext) -> SliceId: #slice_id = context_client.SetSlice(request) #if len(request.slice_endpoint_ids) != 2: return slice_id @@ -186,7 +184,7 @@ class SliceServiceServicerImpl(SliceServiceServicer): # raise NotImplementedError('Slice should create local services for single domain slice') return self.create_update(request) - @safe_and_metered_rpc_method(METRICS, LOGGER) + @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def DeleteSlice(self, request : SliceId, context : grpc.ServicerContext) -> Empty: context_client = ContextClient() try: -- GitLab From 1829036f92ca4b897effb4f47362d4637b907392 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 30 Nov 2022 19:04:14 +0000 Subject: [PATCH 062/353] Common: - updated grafana dashboard backups and prometheus queries --- .../grafana_prometheus_component_rpc.json | 394 ++++++++++++++++++ .../grafana_prometheus_device_driver.json | 28 +- .../grafana_prometheus_service_handler.json | 32 +- .../tests/prometheus_queries.txt | 53 ++- 4 files changed, 461 insertions(+), 46 deletions(-) diff --git a/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json b/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json index e69de29bb..33aba15b6 100644 --- a/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json +++ b/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json @@ -0,0 +1,394 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 27, + "iteration": 1669834289148, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "prometheus", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.4", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "tfs_[[component]]_rpc_[[method]]_counter_requests_started_total", + "interval": "", + "legendFormat": "{{__name__}}", + "queryType": "randomWalk", + "refId": "A" + }, + { + "exemplar": true, + "expr": "tfs_[[component]]_rpc_[[method]]_counter_requests_completed_total", + "hide": false, + "interval": "", + "legendFormat": "{{__name__}}", + "refId": "B" + }, + { + "exemplar": true, + "expr": "tfs_[[component]]_rpc_[[method]]_counter_requests_failed_total", + "hide": false, + "interval": "", + "legendFormat": "{{__name__}}", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transformations": [ + { + "id": "renameByRegex", + "options": { + "regex": "service_.*_counter_requests_(.*)_total", + "renamePattern": "$1" + } + } + ], + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:935", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:936", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "prometheus", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 6 + }, + "hiddenSeries": false, + "id": 5, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.4", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "tfs_[[component]]_rpc_[[method]]_histogram_duration_sum", + "hide": false, + "interval": "", + "legendFormat": "total time", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Total Exec Time", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "transformations": [], + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:407", + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "$$hashKey": "object:408", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "cards": { + "cardPadding": null, + "cardRound": null + }, + "color": { + "cardColor": "#b4ff00", + "colorScale": "sqrt", + "colorScheme": "interpolateRdYlGn", + "exponent": 0.5, + "max": null, + "min": 0, + "mode": "opacity" + }, + "dataFormat": "tsbuckets", + "datasource": "prometheus", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 12 + }, + "heatmap": {}, + "hideZeroBuckets": true, + "highlightCards": true, + "id": 2, + "legend": { + "show": false + }, + "pluginVersion": "7.5.4", + "reverseYBuckets": false, + "targets": [ + { + "exemplar": true, + "expr": "sum(increase(tfs_[[component]]_rpc_[[method]]_histogram_duration_bucket[$__rate_interval])) by (le)", + "format": "heatmap", + "instant": false, + "interval": "", + "intervalFactor": 1, + "legendFormat": "{{le}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Histogram", + "tooltip": { + "show": true, + "showHistogram": true + }, + "type": "heatmap", + "xAxis": { + "show": true + }, + "xBucketNumber": null, + "xBucketSize": null, + "yAxis": { + "decimals": null, + "format": "s", + "logBase": 1, + "max": null, + "min": null, + "show": true, + "splitFactor": null + }, + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null + } + ], + "refresh": false, + "schemaVersion": 27, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": { + "selected": false, + "text": "device", + "value": "device" + }, + "datasource": "prometheus", + "definition": "metrics(tfs_)", + "description": null, + "error": null, + "hide": 0, + "includeAll": false, + "label": "Component", + "multi": false, + "name": "component", + "options": [], + "query": { + "query": "metrics(tfs_)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/tfs_(.+)_rpc_.*/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": { + "selected": false, + "text": "adddevice", + "value": "adddevice" + }, + "datasource": "prometheus", + "definition": "metrics(tfs_[[component]]_rpc_)", + "description": null, + "error": null, + "hide": 0, + "includeAll": false, + "label": "Method", + "multi": false, + "name": "method", + "options": [], + "query": { + "query": "metrics(tfs_[[component]]_rpc_)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/tfs_[[component]]_rpc_(.+)_histogram_duration_bucket/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "TFS / Component RPCs", + "uid": "KKxzxIFVz", + "version": 7 +} \ No newline at end of file diff --git a/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json b/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json index 32d4c0d59..629cf0140 100644 --- a/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json +++ b/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json @@ -16,7 +16,7 @@ "gnetId": null, "graphTooltip": 0, "id": 25, - "iteration": 1669828816450, + "iteration": 1669834795409, "links": [], "panels": [ { @@ -66,7 +66,7 @@ "targets": [ { "exemplar": true, - "expr": "device_driver_[[method]]_counter_requests_started_total{driver=\"[[driver]]\"}", + "expr": "tfs_device_driver_[[method]]_counter_requests_started_total{driver=\"[[driver]]\"}", "interval": "", "legendFormat": "{{__name__}}", "queryType": "randomWalk", @@ -74,7 +74,7 @@ }, { "exemplar": true, - "expr": "device_driver_[[method]]_counter_requests_completed_total{driver=\"[[driver]]\"}", + "expr": "tfs_device_driver_[[method]]_counter_requests_completed_total{driver=\"[[driver]]\"}", "hide": false, "interval": "", "legendFormat": "{{__name__}}", @@ -82,7 +82,7 @@ }, { "exemplar": true, - "expr": "device_driver_[[method]]_counter_requests_failed_total{driver=\"[[driver]]\"}", + "expr": "tfs_device_driver_[[method]]_counter_requests_failed_total{driver=\"[[driver]]\"}", "hide": false, "interval": "", "legendFormat": "{{__name__}}", @@ -103,7 +103,7 @@ { "id": "renameByRegex", "options": { - "regex": "device_.*_counter_requests_(.*)_total", + "regex": "tfs_device_driver_.*_counter_requests_(.*)_total", "renamePattern": "$1" } } @@ -188,7 +188,7 @@ "targets": [ { "exemplar": true, - "expr": "device_driver_[[method]]_histogram_duration_sum{driver=\"[[driver]]\"}", + "expr": "tfs_device_driver_[[method]]_histogram_duration_sum{driver=\"[[driver]]\"}", "hide": false, "interval": "", "legendFormat": "total time", @@ -277,7 +277,7 @@ "targets": [ { "exemplar": true, - "expr": "sum(increase(device_driver_[[method]]_histogram_duration_bucket{driver=\"[[driver]]\"}[$__rate_interval])) by (le)", + "expr": "sum(increase(tfs_device_driver_[[method]]_histogram_duration_bucket{driver=\"[[driver]]\"}[$__rate_interval])) by (le)", "format": "heatmap", "instant": false, "interval": "", @@ -326,7 +326,7 @@ "value": "setconfig" }, "datasource": "prometheus", - "definition": "metrics(.+)", + "definition": "metrics(tfs_device_driver_.+)", "description": null, "error": null, "hide": 0, @@ -336,11 +336,11 @@ "name": "method", "options": [], "query": { - "query": "metrics(.+)", + "query": "metrics(tfs_device_driver_.+)", "refId": "StandardVariableQuery" }, "refresh": 2, - "regex": "/device_driver_(.+config)_histogram_duration_bucket/", + "regex": "/tfs_device_driver_(.+config)_histogram_duration_bucket/", "skipUrlSync": false, "sort": 0, "tagValuesQuery": "", @@ -357,7 +357,7 @@ "value": "emulated" }, "datasource": "prometheus", - "definition": "label_values(device_driver_[[method]]_histogram_duration_bucket, driver)", + "definition": "label_values(tfs_device_driver_[[method]]_histogram_duration_bucket, driver)", "description": null, "error": null, "hide": 0, @@ -367,7 +367,7 @@ "name": "driver", "options": [], "query": { - "query": "label_values(device_driver_[[method]]_histogram_duration_bucket, driver)", + "query": "label_values(tfs_device_driver_[[method]]_histogram_duration_bucket, driver)", "refId": "StandardVariableQuery" }, "refresh": 2, @@ -383,12 +383,12 @@ ] }, "time": { - "from": "now-1h", + "from": "now-30m", "to": "now" }, "timepicker": {}, "timezone": "", "title": "TFS / Device / Driver", "uid": "eAg-wsOVk", - "version": 15 + "version": 17 } \ No newline at end of file diff --git a/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json b/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json index d7e55e271..299b01a7b 100644 --- a/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json +++ b/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json @@ -16,7 +16,7 @@ "gnetId": null, "graphTooltip": 0, "id": 26, - "iteration": 1669829507595, + "iteration": 1669834937164, "links": [], "panels": [ { @@ -66,7 +66,7 @@ "targets": [ { "exemplar": true, - "expr": "service_handler_[[method]]_counter_requests_started_total{handler=\"[[handler]]\"}", + "expr": "tfs_service_handler_[[method]]_counter_requests_started_total{handler=\"[[handler]]\"} ", "interval": "", "legendFormat": "{{__name__}}", "queryType": "randomWalk", @@ -74,7 +74,7 @@ }, { "exemplar": true, - "expr": "service_handler_[[method]]_counter_requests_completed_total{handler=\"[[handler]]\"}", + "expr": "tfs_service_handler_[[method]]_counter_requests_completed_total{handler=\"[[handler]]\"}", "hide": false, "interval": "", "legendFormat": "{{__name__}}", @@ -82,7 +82,7 @@ }, { "exemplar": true, - "expr": "service_handler_[[method]]_counter_requests_failed_total{handler=\"[[handler]]\"}", + "expr": "tfs_service_handler_[[method]]_counter_requests_failed_total{handler=\"[[handler]]\"}", "hide": false, "interval": "", "legendFormat": "{{__name__}}", @@ -103,7 +103,7 @@ { "id": "renameByRegex", "options": { - "regex": "service_.*_counter_requests_(.*)_total", + "regex": "tfs_service_handler_.*_counter_requests_(.*)_total", "renamePattern": "$1" } } @@ -188,7 +188,7 @@ "targets": [ { "exemplar": true, - "expr": "service_handler_[[method]]_histogram_duration_sum{handler=\"[[handler]]\"}", + "expr": "tfs_service_handler_[[method]]_histogram_duration_sum{handler=\"[[handler]]\"}", "hide": false, "interval": "", "legendFormat": "total time", @@ -277,7 +277,7 @@ "targets": [ { "exemplar": true, - "expr": "sum(increase(service_handler_[[method]]_histogram_duration_bucket{handler=\"[[handler]]\"}[$__rate_interval])) by (le)", + "expr": "sum(increase(tfs_service_handler_[[method]]_histogram_duration_bucket{handler=\"[[handler]]\"}[$__rate_interval])) by (le)", "format": "heatmap", "instant": false, "interval": "", @@ -312,7 +312,7 @@ "yBucketSize": null } ], - "refresh": false, + "refresh": "5s", "schemaVersion": 27, "style": "dark", "tags": [], @@ -321,12 +321,12 @@ { "allValue": null, "current": { - "selected": true, + "selected": false, "text": "setconfig", "value": "setconfig" }, "datasource": "prometheus", - "definition": "metrics(.+)", + "definition": "metrics(tfs_service_handler_.+)", "description": null, "error": null, "hide": 0, @@ -336,11 +336,11 @@ "name": "method", "options": [], "query": { - "query": "metrics(.+)", + "query": "metrics(tfs_service_handler_.+)", "refId": "StandardVariableQuery" }, "refresh": 2, - "regex": "/service_handler_(.+config)_histogram_duration_bucket/", + "regex": "/tfs_service_handler_(.+config)_histogram_duration_bucket/", "skipUrlSync": false, "sort": 0, "tagValuesQuery": "", @@ -357,7 +357,7 @@ "value": "l2nm_emulated" }, "datasource": "prometheus", - "definition": "label_values(service_handler_[[method]]_histogram_duration_bucket, handler)", + "definition": "label_values(tfs_service_handler_[[method]]_histogram_duration_bucket, handler)", "description": null, "error": null, "hide": 0, @@ -367,7 +367,7 @@ "name": "handler", "options": [], "query": { - "query": "label_values(service_handler_[[method]]_histogram_duration_bucket, handler)", + "query": "label_values(tfs_service_handler_[[method]]_histogram_duration_bucket, handler)", "refId": "StandardVariableQuery" }, "refresh": 2, @@ -383,12 +383,12 @@ ] }, "time": { - "from": "now-1h", + "from": "now-30m", "to": "now" }, "timepicker": {}, "timezone": "", "title": "TFS / Service / Handler", "uid": "DNOhOIF4k", - "version": 6 + "version": 10 } \ No newline at end of file diff --git a/src/common/method_wrappers/tests/prometheus_queries.txt b/src/common/method_wrappers/tests/prometheus_queries.txt index 80f369934..be44ad105 100644 --- a/src/common/method_wrappers/tests/prometheus_queries.txt +++ b/src/common/method_wrappers/tests/prometheus_queries.txt @@ -1,31 +1,52 @@ +TFS/Components: +--------------- variables: +name=component + query=metrics(tfs_) + regex=/tfs_(.+)_rpc_.*/ name=method - query=metrics(.+) - regex=/device_driver_(.+config)_histogram_duration_bucket/ + query=metrics(tfs_[[component]]_rpc_) + regex=/tfs_[[component]]_rpc_(.+)_histogram_duration_bucket/ + +plots: +tfs_[[component]]_rpc_[[method]]_counter_requests_started_total +tfs_[[component]]_rpc_[[method]]_counter_requests_completed_total +tfs_[[component]]_rpc_[[method]]_counter_requests_failed_total +tfs_[[component]]_rpc_[[method]]_histogram_duration_sum +sum(increase(tfs_[[component]]_rpc_[[method]]_histogram_duration_bucket[$__rate_interval])) by (le) + + +TFS/Device/Driver: +------------------ + +variables: +name=method + query=metrics(tfs_device_driver_.+) + regex=/tfs_device_driver_(.+config)_histogram_duration_bucket/ name=driver - query=label_values(device_driver_[[method]]_histogram_duration_bucket, driver) + query=label_values(tfs_device_driver_[[method]]_histogram_duration_bucket, driver) regex= plots: -device_driver_[[method]]_counter_requests_started_total{driver="[[driver]]"} -device_driver_[[method]]_counter_requests_completed_total{driver="[[driver]]"} -device_driver_[[method]]_counter_requests_failed_total{driver="[[driver]]"} -device_driver_[[method]]_histogram_duration_sum{driver="[[driver]]"} -sum(increase(device_driver_[[method]]_histogram_duration_bucket{driver="[[driver]]"}[$__rate_interval])) by (le) +tfs_device_driver_[[method]]_counter_requests_started_total{driver="[[driver]]"} +tfs_device_driver_[[method]]_counter_requests_completed_total{driver="[[driver]]"} +tfs_device_driver_[[method]]_counter_requests_failed_total{driver="[[driver]]"} +tfs_device_driver_[[method]]_histogram_duration_sum{driver="[[driver]]"} +sum(increase(tfs_device_driver_[[method]]_histogram_duration_bucket{driver="[[driver]]"}[$__rate_interval])) by (le) variables: name=method - query=metrics(.+) - regex=/service_handler_(.+config)_histogram_duration_bucket/ + query=metrics(tfs_service_handler_.+) + regex=/tfs_service_handler_(.+config)_histogram_duration_bucket/ name=driver - query=label_values(service_handler_[[method]]_histogram_duration_bucket, handler) + query=label_values(tfs_service_handler_[[method]]_histogram_duration_bucket, handler) regex= plots: -service_handler_[[method]]_counter_requests_started_total{handler="[[handler]]"} -service_handler_[[method]]_counter_requests_completed_total{handler="[[handler]]"} -service_handler_[[method]]_counter_requests_failed_total{handler="[[handler]]"} -service_handler_[[method]]_histogram_duration_sum{handler="[[handler]]"} -sum(increase(service_handler_[[method]]_histogram_duration_bucket{handler="[[handler]]"}[$__rate_interval])) by (le) +tfs_service_handler_[[method]]_counter_requests_started_total{handler="[[handler]]"} +tfs_service_handler_[[method]]_counter_requests_completed_total{handler="[[handler]]"} +tfs_service_handler_[[method]]_counter_requests_failed_total{handler="[[handler]]"} +tfs_service_handler_[[method]]_histogram_duration_sum{handler="[[handler]]"} +sum(increase(tfs_service_handler_[[method]]_histogram_duration_bucket{handler="[[handler]]"}[$__rate_interval])) by (le) -- GitLab From 94e65624335556b58baed84f3d7f85ef675515ab Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 2 Dec 2022 12:10:33 +0000 Subject: [PATCH 063/353] Tools: - added tool to load scenarios from descriptor files - added README.md file - added helper script - added example descriptors --- src/tests/tools/load_scenario/README.md | 17 ++ src/tests/tools/load_scenario/__init__.py | 14 ++ src/tests/tools/load_scenario/__main__.py | 37 +++ .../load_scenario/example_descriptors.json | 229 ++++++++++++++++++ src/tests/tools/load_scenario/run.sh | 4 + 5 files changed, 301 insertions(+) create mode 100644 src/tests/tools/load_scenario/README.md create mode 100644 src/tests/tools/load_scenario/__init__.py create mode 100644 src/tests/tools/load_scenario/__main__.py create mode 100644 src/tests/tools/load_scenario/example_descriptors.json create mode 100755 src/tests/tools/load_scenario/run.sh diff --git a/src/tests/tools/load_scenario/README.md b/src/tests/tools/load_scenario/README.md new file mode 100644 index 000000000..3845cbf01 --- /dev/null +++ b/src/tests/tools/load_scenario/README.md @@ -0,0 +1,17 @@ +# Tool: Load Scenario: + +Simple tool to populate ETSI TeraFlowSDN controller with same descriptors that can be loaded through the WebUI. + +## Example: + +Deploy TeraFlowSDN controller with your specific settings: +```(bash) +cd ~/tfs-ctrl +source my_deploy.sh +./deploy.sh +``` + +Populate TeraFlowSDN controller with your descriptor file: +```(bash) +./src/tests/tools/load_scenario/run.sh src/tests/tools/load_scenario/example_descriptors.json +``` diff --git a/src/tests/tools/load_scenario/__init__.py b/src/tests/tools/load_scenario/__init__.py new file mode 100644 index 000000000..70a332512 --- /dev/null +++ b/src/tests/tools/load_scenario/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/tests/tools/load_scenario/__main__.py b/src/tests/tools/load_scenario/__main__.py new file mode 100644 index 000000000..f2cd11919 --- /dev/null +++ b/src/tests/tools/load_scenario/__main__.py @@ -0,0 +1,37 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, sys +from common.tests.LoadScenario import load_scenario_from_descriptor +from context.client.ContextClient import ContextClient +from device.client.DeviceClient import DeviceClient +from service.client.ServiceClient import ServiceClient +from slice.client.SliceClient import SliceClient + +logging.basicConfig(level=logging.INFO) +LOGGER = logging.getLogger(__name__) + +def main(): + context_client = ContextClient() + device_client = DeviceClient() + service_client = ServiceClient() + slice_client = SliceClient() + + LOGGER.info('Loading scenario...') + load_scenario_from_descriptor(sys.argv[1], context_client, device_client, service_client, slice_client) + LOGGER.info('Done!') + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/tests/tools/load_scenario/example_descriptors.json b/src/tests/tools/load_scenario/example_descriptors.json new file mode 100644 index 000000000..5fb0c0867 --- /dev/null +++ b/src/tests/tools/load_scenario/example_descriptors.json @@ -0,0 +1,229 @@ +{ + "contexts": [ + { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_ids": [], "service_ids": [] + } + ], + "topologies": [ + { + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "admin"} + }, + "device_ids": [ + {"device_uuid": {"uuid": "R1"}}, + {"device_uuid": {"uuid": "R2"}}, + {"device_uuid": {"uuid": "R3"}}, + {"device_uuid": {"uuid": "R4"}}, + {"device_uuid": {"uuid": "R5"}}, + {"device_uuid": {"uuid": "R6"}}, + {"device_uuid": {"uuid": "R7"}} + ], + "link_ids": [ + {"link_uuid": {"uuid": "R1==R2"}}, + {"link_uuid": {"uuid": "R2==R3"}}, + {"link_uuid": {"uuid": "R3==R4"}}, + {"link_uuid": {"uuid": "R4==R5"}}, + {"link_uuid": {"uuid": "R5==R6"}}, + {"link_uuid": {"uuid": "R6==R1"}}, + {"link_uuid": {"uuid": "R1==R7"}}, + {"link_uuid": {"uuid": "R3==R7"}}, + {"link_uuid": {"uuid": "R5==R7"}} + ] + } + ], + "devices": [ + { + "device_id": {"device_uuid": {"uuid": "R1"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R2"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R3"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R4"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R5"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R6"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R7"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"}, + {"sample_types": [], "type": "copper", "uuid": "2/4"}, + {"sample_types": [], "type": "copper", "uuid": "2/5"}, + {"sample_types": [], "type": "copper", "uuid": "2/6"} + ]}}} + ]} + } + ], + "links": [ + { + "link_id": {"link_uuid": {"uuid": "R1==R2"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R2==R3"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R3==R4"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R4==R5"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R5==R6"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R6"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R6==R1"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R6"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R1==R7"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/3"}}, + {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R3==R7"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/3"}}, + {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/3"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R5==R7"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/3"}}, + {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/5"}} + ] + } + ] +} \ No newline at end of file diff --git a/src/tests/tools/load_scenario/run.sh b/src/tests/tools/load_scenario/run.sh new file mode 100755 index 000000000..5d659c189 --- /dev/null +++ b/src/tests/tools/load_scenario/run.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +source tfs_runtime_env_vars.sh +python -m tests.tools.load_scenario $1 -- GitLab From deb4e0965804f5084c5006440de3adcc4f8dd490 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 2 Dec 2022 12:19:57 +0000 Subject: [PATCH 064/353] Manifests: - moved manifests for service monitors --- .../method_wrappers/tests => manifests}/servicemonitors.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {src/common/method_wrappers/tests => manifests}/servicemonitors.yaml (100%) diff --git a/src/common/method_wrappers/tests/servicemonitors.yaml b/manifests/servicemonitors.yaml similarity index 100% rename from src/common/method_wrappers/tests/servicemonitors.yaml rename to manifests/servicemonitors.yaml -- GitLab From 2f520913862c7a0ae55f23f8588c199bf6e57df8 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 2 Dec 2022 13:38:41 +0000 Subject: [PATCH 065/353] Common: - Increased default gRPC workers from 10 to 200 - added object factory methods for L2NM services --- src/common/Constants.py | 2 +- src/common/tools/object_factory/Service.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/common/Constants.py b/src/common/Constants.py index 964d904da..ffdfbc4e0 100644 --- a/src/common/Constants.py +++ b/src/common/Constants.py @@ -20,7 +20,7 @@ DEFAULT_LOG_LEVEL = logging.WARNING # Default gRPC server settings DEFAULT_GRPC_BIND_ADDRESS = '0.0.0.0' -DEFAULT_GRPC_MAX_WORKERS = 10 +DEFAULT_GRPC_MAX_WORKERS = 200 DEFAULT_GRPC_GRACE_PERIOD = 60 # Default HTTP server settings diff --git a/src/common/tools/object_factory/Service.py b/src/common/tools/object_factory/Service.py index 62f3dcbda..829c2f667 100644 --- a/src/common/tools/object_factory/Service.py +++ b/src/common/tools/object_factory/Service.py @@ -42,6 +42,16 @@ def json_service( 'service_config' : {'config_rules': copy.deepcopy(config_rules)}, } +def json_service_l2nm_planned( + service_uuid : str, endpoint_ids : List[Dict] = [], constraints : List[Dict] = [], + config_rules : List[Dict] = [], context_uuid : str = DEFAULT_CONTEXT_UUID + ): + + return json_service( + service_uuid, ServiceTypeEnum.SERVICETYPE_L2NM, context_id=json_context_id(context_uuid), + status=ServiceStatusEnum.SERVICESTATUS_PLANNED, endpoint_ids=endpoint_ids, constraints=constraints, + config_rules=config_rules) + def json_service_l3nm_planned( service_uuid : str, endpoint_ids : List[Dict] = [], constraints : List[Dict] = [], config_rules : List[Dict] = [], context_uuid : str = DEFAULT_CONTEXT_UUID -- GitLab From 817351a5d0b600fc9388cf62979a79e19048d41c Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 2 Dec 2022 13:40:18 +0000 Subject: [PATCH 066/353] PathComp component: Backend: - Increased number of allowed edges from 10 to 20 Frontend: - reduced log levels for messages - added mutex to prevent issuing multiple calls to the backend at the same time --- src/pathcomp/backend/pathComp_tools.h | 2 +- .../frontend/service/PathCompServiceServicerImpl.py | 11 +++++++---- .../frontend/service/algorithms/_Algorithm.py | 8 ++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/pathcomp/backend/pathComp_tools.h b/src/pathcomp/backend/pathComp_tools.h index 8fe704c39..adbbf30c4 100644 --- a/src/pathcomp/backend/pathComp_tools.h +++ b/src/pathcomp/backend/pathComp_tools.h @@ -121,7 +121,7 @@ struct map_nodes_t { }; #define MAX_NUM_VERTICES 20 // 100 # LGR: reduced from 100 to 20 to divide by 5 the memory used -#define MAX_NUM_EDGES 10 // 100 # LGR: reduced from 100 to 10 to divide by 10 the memory used +#define MAX_NUM_EDGES 20 // 100 # LGR: reduced from 100 to 20 to divide by 5 the memory used // Structures for the graph composition struct targetNodes_t { // remote / targeted node diff --git a/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py b/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py index 4f725cce7..ca4132754 100644 --- a/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py +++ b/src/pathcomp/frontend/service/PathCompServiceServicerImpl.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import grpc, logging +import grpc, logging, threading from common.Constants import DEFAULT_CONTEXT_UUID, INTERDOMAIN_TOPOLOGY_UUID from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method from common.proto.context_pb2 import ContextId, Empty @@ -35,11 +35,12 @@ ADMIN_CONTEXT_ID = ContextId(**json_context_id(DEFAULT_CONTEXT_UUID)) class PathCompServiceServicerImpl(PathCompServiceServicer): def __init__(self) -> None: LOGGER.debug('Creating Servicer...') + self._lock = threading.Lock() LOGGER.debug('Servicer Created') @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def Compute(self, request : PathCompRequest, context : grpc.ServicerContext) -> PathCompReply: - LOGGER.info('[Compute] begin ; request = {:s}'.format(grpc_message_to_json_string(request))) + LOGGER.debug('[Compute] begin ; request = {:s}'.format(grpc_message_to_json_string(request))) context_client = ContextClient() @@ -66,8 +67,10 @@ class PathCompServiceServicerImpl(PathCompServiceServicer): #import time #ts = time.time() #algorithm.execute('request-{:f}.json'.format(ts), 'reply-{:f}.json'.format(ts)) - algorithm.execute() + with self._lock: + # ensure backend receives requests one at a time + algorithm.execute() reply = algorithm.get_reply() - LOGGER.info('[Compute] end ; reply = {:s}'.format(grpc_message_to_json_string(reply))) + LOGGER.debug('[Compute] end ; reply = {:s}'.format(grpc_message_to_json_string(reply))) return reply diff --git a/src/pathcomp/frontend/service/algorithms/_Algorithm.py b/src/pathcomp/frontend/service/algorithms/_Algorithm.py index 383364245..a24ef7693 100644 --- a/src/pathcomp/frontend/service/algorithms/_Algorithm.py +++ b/src/pathcomp/frontend/service/algorithms/_Algorithm.py @@ -93,22 +93,22 @@ class _Algorithm: def execute(self, dump_request_filename : Optional[str] = None, dump_reply_filename : Optional[str] = None) -> None: request = {'serviceList': self.service_list, 'deviceList': self.device_list, 'linkList': self.link_list} - self.logger.info('[execute] request={:s}'.format(str(request))) + self.logger.debug('[execute] request={:s}'.format(str(request))) if dump_request_filename is not None: with open(dump_request_filename, 'w', encoding='UTF-8') as f: f.write(json.dumps(request, sort_keys=True, indent=4)) - self.logger.info('[execute] BACKEND_URL: {:s}'.format(str(BACKEND_URL))) + self.logger.debug('[execute] BACKEND_URL: {:s}'.format(str(BACKEND_URL))) reply = requests.post(BACKEND_URL, json=request) self.status_code = reply.status_code self.raw_reply = reply.content.decode('UTF-8') - self.logger.info('[execute] status_code={:s} reply={:s}'.format(str(reply.status_code), str(self.raw_reply))) + self.logger.debug('[execute] status_code={:s} reply={:s}'.format(str(reply.status_code), str(self.raw_reply))) if dump_reply_filename is not None: with open(dump_reply_filename, 'w', encoding='UTF-8') as f: f.write('status_code={:s} reply={:s}'.format(str(self.status_code), str(self.raw_reply))) - if reply.status_code not in {requests.codes.ok}: + if reply.status_code not in {requests.codes.ok}: # pylint: disable=no-member raise Exception('Backend error({:s}) for request({:s})'.format( str(self.raw_reply), json.dumps(request, sort_keys=True))) -- GitLab From 03864859bf21fef27da2f7f47f6720375d1dced1 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 2 Dec 2022 13:41:19 +0000 Subject: [PATCH 067/353] Deploy script: - removed scaledown+scaleup during deploy (useless) --- deploy.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deploy.sh b/deploy.sh index d9b1d69d6..c62778417 100755 --- a/deploy.sh +++ b/deploy.sh @@ -200,8 +200,8 @@ for COMPONENT in $TFS_COMPONENTS; do DEPLOY_LOG="$TMP_LOGS_FOLDER/deploy_${COMPONENT}.log" kubectl --namespace $TFS_K8S_NAMESPACE apply -f "$MANIFEST" > "$DEPLOY_LOG" COMPONENT_OBJNAME=$(echo "${COMPONENT}" | sed "s/\_/-/") - kubectl --namespace $TFS_K8S_NAMESPACE scale deployment --replicas=0 ${COMPONENT_OBJNAME}service >> "$DEPLOY_LOG" - kubectl --namespace $TFS_K8S_NAMESPACE scale deployment --replicas=1 ${COMPONENT_OBJNAME}service >> "$DEPLOY_LOG" + #kubectl --namespace $TFS_K8S_NAMESPACE scale deployment --replicas=0 ${COMPONENT_OBJNAME}service >> "$DEPLOY_LOG" + #kubectl --namespace $TFS_K8S_NAMESPACE scale deployment --replicas=1 ${COMPONENT_OBJNAME}service >> "$DEPLOY_LOG" echo " Collecting env-vars for '$COMPONENT' component..." -- GitLab From 38cd8b5df9e0389f60e1793f4092e8881eaaf851 Mon Sep 17 00:00:00 2001 From: longllu Date: Fri, 2 Dec 2022 14:17:32 +0000 Subject: [PATCH 068/353] WebUI: - script for starting the WebUI in developing mode --- src/start_webui_dev_mode.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/start_webui_dev_mode.sh b/src/start_webui_dev_mode.sh index 74540bcb3..8c45cd89e 100755 --- a/src/start_webui_dev_mode.sh +++ b/src/start_webui_dev_mode.sh @@ -14,19 +14,23 @@ # for development purposes only -K8S_NAMESPACE=${K8S_NAMESPACE:-'tf-dev'} +# K8S_NAMESPACE=${K8S_NAMESPACE:-'tf-dev'} -export CONTEXTSERVICE_SERVICE_HOST=`kubectl get service/contextservice -n ${K8S_NAMESPACE} -o jsonpath='{.spec.clusterIP}'` +# export CONTEXTSERVICE_SERVICE_HOST=`kubectl get service/contextservice -n ${K8S_NAMESPACE} -o jsonpath='{.spec.clusterIP}'` -echo Context IP: $CONTEXTSERVICE_SERVICE_HOST +# echo Context IP: $CONTEXTSERVICE_SERVICE_HOST -export DEVICESERVICE_SERVICE_HOST=`kubectl get service/deviceservice -n ${K8S_NAMESPACE} -o jsonpath='{.spec.clusterIP}'` +# export DEVICESERVICE_SERVICE_HOST=`kubectl get service/deviceservice -n ${K8S_NAMESPACE} -o jsonpath='{.spec.clusterIP}'` -echo Device IP: $DEVICESERVICE_SERVICE_HOST +# echo Device IP: $DEVICESERVICE_SERVICE_HOST + +source tfs_runtime_env_vars.sh export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION='python' export HOST="127.0.0.1" +export HOSTNAME="test" export FLASK_ENV="development" +export LOG_LEVEL="DEBUG" # python3 -m webbrowser http://${HOST}:8004 -- GitLab From 95e19c7ee95a032476ed53abc9333d8231361b84 Mon Sep 17 00:00:00 2001 From: longllu Date: Fri, 2 Dec 2022 14:30:58 +0000 Subject: [PATCH 069/353] WebUI: - Improving the Add device form - Updating the drivers list of the Add device form --- src/webui/service/device/forms.py | 36 ++- src/webui/service/device/routes.py | 85 ++++--- src/webui/service/templates/device/add.html | 215 +++++++++++------- .../service/templates/device/detail.html | 194 ++++++++-------- 4 files changed, 292 insertions(+), 238 deletions(-) diff --git a/src/webui/service/device/forms.py b/src/webui/service/device/forms.py index d1880d321..3d728ade1 100644 --- a/src/webui/service/device/forms.py +++ b/src/webui/service/device/forms.py @@ -14,7 +14,7 @@ # external imports from flask_wtf import FlaskForm -from wtforms import StringField, SelectField, TextAreaField, SubmitField +from wtforms import StringField, SelectField, TextAreaField, SubmitField, BooleanField, Form from wtforms.validators import DataRequired, Length, NumberRange, Regexp, ValidationError from common.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum from webui.utils.form_validators import key_value_validator @@ -22,33 +22,23 @@ from webui.utils.form_validators import key_value_validator class AddDeviceForm(FlaskForm): device_id = StringField('ID', validators=[DataRequired(), Length(min=5)]) - device_type = StringField('Type', - validators=[DataRequired(), Length(min=5)]) - device_config = TextAreaField('Configurations', validators=[key_value_validator()]) + device_type = SelectField('Type', choices = []) operational_status = SelectField('Operational Status', # choices=[(-1, 'Select...'), (0, 'Undefined'), (1, 'Disabled'), (2, 'Enabled')], coerce=int, validators=[NumberRange(min=0)]) - device_drivers = TextAreaField('Drivers', validators=[DataRequired(), Regexp(r'^\d+(,\d+)*$')]) + device_drivers_undefined = BooleanField('UNDEFINED') + device_drivers_openconfig = BooleanField('OPENCONFIG') + device_drivers_transport_api = BooleanField('TRANSPORT_API') + device_drivers_p4 = BooleanField('P4') + device_drivers_ietf_network_topology = BooleanField('IETF_NETWORK_TOPOLOGY') + device_drivers_onf_tr_352 = BooleanField('ONF_TR_352') + device_drivers_xr = BooleanField('XR') + device_config_address = StringField('connect/address',default='127.0.0.1',validators=[DataRequired(), Length(min=5)]) + device_config_port = StringField('connect/port',default='0',validators=[DataRequired(), Length(min=1)]) + device_config_settings = TextAreaField('connect/settings',default='{}',validators=[DataRequired(), Length(min=2)]) submit = SubmitField('Add') def validate_operational_status(form, field): if field.data not in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_number: - raise ValidationError('The operational status value selected is incorrect!') - - def validate_device_drivers(form, field): - if ',' not in field.data: - data = str(field.data) + ',' - else: - data = field.data - for value in data.split(','): - value = value.strip() - if len(value) == 0: - continue - try: - value_int = int(value) - except: - raise ValidationError(f'The value "{value}" is not a valid driver identified.') - if value_int not in DeviceDriverEnum.DESCRIPTOR.values_by_number: - values = ', '.join([str(x) for x in DeviceDriverEnum.DESCRIPTOR.values_by_number]) - raise ValidationError(f'The device driver {value_int} is not correct. Allowed values are: {values}.') + raise ValidationError('The operational status value selected is incorrect!') \ No newline at end of file diff --git a/src/webui/service/device/routes.py b/src/webui/service/device/routes.py index b57c5735d..220a3a33c 100644 --- a/src/webui/service/device/routes.py +++ b/src/webui/service/device/routes.py @@ -16,12 +16,14 @@ from flask import current_app, render_template, Blueprint, flash, session, redir from common.proto.context_pb2 import ( ConfigActionEnum, ConfigRule, Device, DeviceDriverEnum, DeviceId, DeviceList, DeviceOperationalStatusEnum, - Empty, TopologyId) + Empty, TopologyId, ContextId) from common.tools.object_factory.Context import json_context_id from common.tools.object_factory.Topology import json_topology_id +from common.tools.context_queries.Device import add_device_to_topology from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from webui.service.device.forms import AddDeviceForm +from common.DeviceTypes import DeviceTypeEnum device = Blueprint('device', __name__, url_prefix='/device') context_client = ContextClient() @@ -57,60 +59,75 @@ def add(): form = AddDeviceForm() # listing enum values - form.operational_status.choices = [(-1, 'Select...')] + form.operational_status.choices = [] for key, value in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_name.items(): form.operational_status.choices.append( (DeviceOperationalStatusEnum.Value(key), key.replace('DEVICEOPERATIONALSTATUS_', ''))) - # device driver ids - device_driver_ids = [] - for key in DeviceDriverEnum.DESCRIPTOR.values_by_name: - device_driver_ids.append(f"{DeviceDriverEnum.Value(key)}={key.replace('DEVICEDRIVER_', '')}") - device_driver_ids = ', '.join(device_driver_ids) + # items for Device Type field + for device_type in DeviceTypeEnum: + form.device_type.choices.append((device_type.value,device_type.value)) if form.validate_on_submit(): device = Device() + # Device UUID: device.device_id.device_uuid.uuid = form.device_id.data - device.device_type = form.device_type.data - if '\n' not in form.device_config.data: - data = form.device_config.data.strip() + '\n' - else: - data = form.device_config.data.strip() - - for config in data.split('\n'): - if len(config.strip()) > 0: - parts = config.strip().split('=') - config_rule: ConfigRule = ConfigRule() - config_rule.action = ConfigActionEnum.CONFIGACTION_SET - config_rule.custom.resource_key = parts[0].strip() - config_rule.custom.resource_value = parts[1].strip() - device.device_config.config_rules.append(config_rule) + # Device type: + device.device_type = str(form.device_type.data) + + # Device configurations: + config_rule = device.device_config.config_rules.add() + config_rule.action = ConfigActionEnum.CONFIGACTION_SET + config_rule.custom.resource_key = '_connect/address' + config_rule.custom.resource_value = form.device_config_address.data + + config_rule = device.device_config.config_rules.add() + config_rule.action = ConfigActionEnum.CONFIGACTION_SET + config_rule.custom.resource_key = '_connect/port' + config_rule.custom.resource_value = form.device_config_port.data + + config_rule = device.device_config.config_rules.add() + config_rule.action = ConfigActionEnum.CONFIGACTION_SET + config_rule.custom.resource_key = '_connect/settings' + config_rule.custom.resource_value = form.device_config_settings.data + + # Device status: device.device_operational_status = form.operational_status.data - if ',' not in form.device_drivers.data: - data = form.device_drivers.data.strip() + ',' - else: - data = form.device_drivers.data.strip() - - for driver in data.split(','): - driver = driver.strip() - if len(driver) == 0: - continue - device.device_drivers.append(int(driver)) + # Device drivers: + if form.device_drivers_undefined.data: + device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_UNDEFINED) + if form.device_drivers_openconfig.data: + device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG) + if form.device_drivers_transport_api.data: + device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API) + if form.device_drivers_p4.data: + device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_P4) + if form.device_drivers_ietf_network_topology.data: + device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY) + if form.device_drivers_onf_tr_352.data: + device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352) + if form.device_drivers_xr.data: + device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_XR) + try: device_client.connect() response: DeviceId = device_client.AddDevice(device) device_client.close() - + context_uuid = session['context_uuid'] + topology_uuid = session['topology_uuid'] + context_client.connect() + context_id = ContextId(**json_context_id(context_uuid)) + add_device_to_topology(context_client, context_id, topology_uuid, device.device_id.device_uuid.uuid) + context_client.close() flash(f'New device was created with ID "{response.device_uuid.uuid}".', 'success') return redirect(url_for('device.home')) except Exception as e: flash(f'Problem adding the device. {e.details()}', 'danger') return render_template('device/add.html', form=form, - submit_text='Add New Device', - device_driver_ids=device_driver_ids) + submit_text='Add New Device') @device.route('detail/', methods=['GET', 'POST']) def detail(device_uuid: str): diff --git a/src/webui/service/templates/device/add.html b/src/webui/service/templates/device/add.html index fe1ba31f2..1acbc7e48 100644 --- a/src/webui/service/templates/device/add.html +++ b/src/webui/service/templates/device/add.html @@ -17,100 +17,145 @@ {% extends 'base.html' %} {% block content %} -

Add New Device

- -
- {{ form.hidden_tag() }} -
-
- {{ form.device_id.label(class="col-sm-2 col-form-label") }} -
- {% if form.device_id.errors %} - {{ form.device_id(class="form-control is-invalid") }} -
- {% for error in form.device_id.errors %} - {{ error }} - {% endfor %} -
- {% else %} - {{ form.device_id(class="form-control") }} - {% endif %} +

Add New Device

+
+ + {{ form.hidden_tag() }} +
+
+ {{ form.device_id.label(class="col-sm-2 col-form-label") }} +
+ {% if form.device_id.errors %} + {{ form.device_id(class="form-control is-invalid") }} +
+ {% for error in form.device_id.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.device_id(class="form-control") }} + {% endif %} +
+
+
+
+ {{ form.device_type.label(class="col-sm-2 col-form-label") }} +
+ {% if form.device_type.errors %} + {{ form.device_type(class="form-control is-invalid") }} +
+ {% for error in form.device_type.errors %} + {{ error }} + {% endfor %}
+ {% else %} + {{ form.device_type(class="form-select")}} + {% endif %}
-
- {{ form.device_type.label(class="col-sm-2 col-form-label") }} -
- {% if form.device_type.errors %} - {{ form.device_type(class="form-control is-invalid") }} -
- {% for error in form.device_type.errors %} - {{ error }} - {% endfor %} -
- {% else %} - {{ form.device_type(class="form-control") }} - {% endif %} +
+
+
+ {{ form.operational_status.label(class="col-sm-2 col-form-label") }} +
+ {% if form.operational_status.errors %} + {{ form.operational_status(class="form-control is-invalid") }} +
+ {% for error in form.operational_status.errors %} + {{ error }} + {% endfor %}
+ {% else %} + {{ form.operational_status(class="form-select") }} + {% endif %}
-
- {{ form.operational_status.label(class="col-sm-2 col-form-label") }} -
- {% if form.operational_status.errors %} - {{ form.operational_status(class="form-control is-invalid") }} -
- {% for error in form.operational_status.errors %} - {{ error }} - {% endfor %} -
- {% else %} - {{ form.operational_status(class="form-control") }} - {% endif %} +
+
+
+
Drivers
+
+ {% if form.device_drivers_undefined.errors %} + {{ form.device_drivers_undefined(class="form-control is-invalid") }} +
+ {% for error in form.device_drivers_undefined.errors %} + {{ error }} + {% endfor %}
+ {% else %} + {{ form.device_drivers_undefined }} {{ form.device_drivers_undefined.label(class="col-sm-3 + col-form-label") }} + {{ form.device_drivers_openconfig }} {{ form.device_drivers_openconfig.label(class="col-sm-3 + col-form-label") }} + {{ form.device_drivers_transport_api }} {{ form.device_drivers_transport_api.label(class="col-sm-3 + col-form-label") }} +
{{ form.device_drivers_p4 }} {{ form.device_drivers_p4.label(class="col-sm-3 col-form-label") }} + {{ form.device_drivers_ietf_network_topology }} {{ + form.device_drivers_ietf_network_topology.label(class="col-sm-3 + col-form-label") }} + {{ form.device_drivers_onf_tr_352 }} {{ form.device_drivers_onf_tr_352.label(class="col-sm-3 + col-form-label") }}
+ {{ form.device_drivers_xr }} {{ form.device_drivers_xr.label(class="col-sm-3 + col-form-label") }} + {% endif %}
-
- {{ form.device_config.label(class="col-sm-2 col-form-label") }} -
- {% if form.device_config.errors %} - {{ form.device_config(class="form-control is-invalid", rows=5) }} -
- {% for error in form.device_config.errors %} - {{ error }} - {% endfor %} -
- {% else %} - {{ form.device_config(class="form-control", rows=5) }} - {% endif %} +
+
+ Configuration Rules
+
+ {{ form.device_config_address.label(class="col-sm-2 col-form-label") }} +
+ {% if form.device_config_address.errors %} + {{ form.device_config_address(class="form-control is-invalid", rows=5) }} +
+ {% for error in form.device_config_address.errors %} + {{ error }} + {% endfor %}
-
The device configurations should follow a key=value format, one configuration per line.
+ {% else %} + {{ form.device_config_address(class="form-control", rows=5) }} + {% endif %}
-
- {{ form.device_drivers.label(class="col-sm-2 col-form-label") }} -
- {% if form.device_drivers.errors %} - {{ form.device_drivers(class="form-control is-invalid", rows=5) }} -
- {% for error in form.device_drivers.errors %} - {{ error }} - {% endfor %} -
- {% else %} - {{ form.device_drivers(class="form-control", rows=5) }} - {% endif %} +
+
+ {{ form.device_config_port.label(class="col-sm-2 col-form-label") }} +
+ {% if form.device_config_port.errors %} + {{ form.device_config_port(class="form-control is-invalid", rows=5) }} +
+ {% for error in form.device_config_port.errors %} + {{ error }} + {% endfor %}
-
- List the device drivers by their numerical ID, separated by commas, without spaces between them. Numerical IDs: {{ device_driver_ids }}. + {% else %} + {{ form.device_config_port(class="form-control", rows=5) }} + {% endif %} +
+
+
+ {{ form.device_config_settings.label(class="col-sm-2 col-form-label") }} +
+ {% if form.device_config_settings.errors %} + {{ form.device_config_settings(class="form-control is-invalid", rows=5) }} +
+ {% for error in form.device_config_settings.errors %} + {{ error }} + {% endfor %}
+ {% else %} + {{ form.device_config_settings(class="form-control", rows=5) }} + {% endif %}
-
- - -
-
- +
+
+
+ + +
+
+ {% endblock %} \ No newline at end of file diff --git a/src/webui/service/templates/device/detail.html b/src/webui/service/templates/device/detail.html index 69ca93727..f07b9c985 100644 --- a/src/webui/service/templates/device/detail.html +++ b/src/webui/service/templates/device/detail.html @@ -13,118 +13,120 @@ See the License for the specific language governing permissions and limitations under the License. --> - - {% extends 'base.html' %} - - {% block content %} -

Device {{ device.device_id.device_uuid.uuid }}

- -
-
- -
- -
- - -
-
-
-
-
- UUID: {{ device.device_id.device_uuid.uuid }}

- Type: {{ device.device_type }}

- Status: {{ dose.Name(device.device_operational_status).replace('DEVICEOPERATIONALSTATUS_', '') }}
- Drivers: -
    - {% for driver in device.device_drivers %} -
  • {{ dde.Name(driver).replace('DEVICEDRIVER_', '').replace('UNDEFINED', 'EMULATED') }}
  • - {% endfor %} -
-
-
- - - - - - - - - {% for endpoint in device.device_endpoints %} - - - - - {% endfor %} - -
EndpointsType
- {{ endpoint.endpoint_id.endpoint_uuid.uuid }} - - {{ endpoint.endpoint_type }} -
-
-
- +{% extends 'base.html' %} + +{% block content %} +

Device {{ device.device_id.device_uuid.uuid }}

+ +
+
+ +
+ +
+ + +
+
- Configurations: +
+
+
+ UUID: {{ device.device_id.device_uuid.uuid }}

+ Type: {{ device.device_type }}

+ Status: {{ dose.Name(device.device_operational_status).replace('DEVICEOPERATIONALSTATUS_', '') }}
+ Drivers: +
    + {% for driver in device.device_drivers %} +
  • {{ dde.Name(driver).replace('DEVICEDRIVER_', '').replace('UNDEFINED', 'EMULATED') }}
  • + {% endfor %} +
+
+
- - + + - {% for config in device.device_config.config_rules %} - {% if config.WhichOneof('config_rule') == 'custom' %} + {% for endpoint in device.device_endpoints %} - {% endif %} {% endfor %}
KeyValueEndpointsType
- {{ config.custom.resource_key }} + {{ endpoint.endpoint_id.endpoint_uuid.uuid }} -
    - {% for key, value in (config.custom.resource_value | from_json).items() %} -
  • {{ key }}: {{ value }}
  • - {% endfor %} -
+ {{ endpoint.endpoint_type }}
+
+
+ +Configurations: + + + + + + + + + {% for config in device.device_config.config_rules %} + {% if config.WhichOneof('config_rule') == 'custom' %} + + + + + {% endif %} + {% endfor %} + +
KeyValue
+ {{ config.custom.resource_key }} + +
    + {% for key, value in (config.custom.resource_value | from_json).items() %} +
  • {{ key }}: {{ value }}
  • + {% endfor %} +
+
- - - - {% endblock %} - \ No newline at end of file + + + + +{% endblock %} -- GitLab From 87818c0775bcee6b2f2a14bba770c871fbd011b8 Mon Sep 17 00:00:00 2001 From: longllu Date: Fri, 2 Dec 2022 14:52:45 +0000 Subject: [PATCH 070/353] WebUI: - Adding link tooltip to the topology --- src/webui/service/templates/js/topology.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/webui/service/templates/js/topology.js b/src/webui/service/templates/js/topology.js index 69de0445d..29156224d 100644 --- a/src/webui/service/templates/js/topology.js +++ b/src/webui/service/templates/js/topology.js @@ -89,6 +89,8 @@ d3.json("{{ url_for('main.topology') }}", function(data) { // node tooltip node.append("title").text(function(d) { return d.id; }); + // link tooltip + link.append("title").text(function(d) { return d.id; }); // link style link -- GitLab From cf2a36a27774250be9cadbf3eef57dbc8e8af1e2 Mon Sep 17 00:00:00 2001 From: cmanso Date: Fri, 2 Dec 2022 15:53:02 +0100 Subject: [PATCH 071/353] Update scalability --- src/context/service/Database.py | 17 +- src/context/service/database/ConfigModel.py | 6 +- src/context/service/database/ContextModel.py | 3 +- src/context/service/database/DeviceModel.py | 42 ++- src/context/service/database/EndPointModel.py | 28 +- .../grpc_server/ContextServiceServicerImpl.py | 249 ++++++++++-------- 6 files changed, 201 insertions(+), 144 deletions(-) diff --git a/src/context/service/Database.py b/src/context/service/Database.py index 8fae9f652..bf970b356 100644 --- a/src/context/service/Database.py +++ b/src/context/service/Database.py @@ -1,7 +1,7 @@ from typing import Tuple, List from sqlalchemy import MetaData -from sqlalchemy.orm import Session +from sqlalchemy.orm import Session, joinedload from context.service.database.Base import Base import logging from common.orm.backend.Tools import key_to_str @@ -27,8 +27,11 @@ class Database(Session): def create_or_update(self, model): with self.session() as session: att = getattr(model, model.main_pk_name()) + obj = self.get_object(type(model), att) + filt = {model.main_pk_name(): att} - found = session.query(type(model)).filter_by(**filt).one_or_none() + t_model = type(model) + found = session.query(t_model).filter_by(**filt).one_or_none() if found: found = True else: @@ -36,6 +39,9 @@ class Database(Session): session.merge(model) session.commit() + + obj = self.get_object(t_model, att) + return model, found def create(self, model): @@ -93,11 +99,11 @@ class Database(Session): raise NotFoundException(model_class.__name__.replace('Model', ''), main_key) return get - def get_or_create(self, model_class: Base, key_parts: List[str] - ) -> Tuple[Base, bool]: + def get_or_create(self, model_class: Base, key_parts: List[str], filt=None) -> Tuple[Base, bool]: str_key = key_to_str(key_parts) - filt = {model_class.main_pk_name(): key_parts} + if not filt: + filt = {model_class.main_pk_name(): key_parts} with self.session() as session: get = session.query(model_class).filter_by(**filt).one_or_none() if get: @@ -105,7 +111,6 @@ class Database(Session): else: obj = model_class() setattr(obj, model_class.main_pk_name(), str_key) - LOGGER.info(obj.dump()) session.add(obj) session.commit() return obj, True diff --git a/src/context/service/database/ConfigModel.py b/src/context/service/database/ConfigModel.py index 4dcd50c2c..40069185f 100644 --- a/src/context/service/database/ConfigModel.py +++ b/src/context/service/database/ConfigModel.py @@ -40,7 +40,7 @@ class ConfigModel(Base): # pylint: disable=abstract-method config_uuid = Column(UUID(as_uuid=False), primary_key=True) # Relationships - config_rule = relationship("ConfigRuleModel", back_populates="config", lazy="dynamic") + config_rule = relationship("ConfigRuleModel", back_populates="config", lazy='joined') def delete(self) -> None: @@ -48,7 +48,7 @@ class ConfigModel(Base): # pylint: disable=abstract-method for pk,_ in db_config_rule_pks: ConfigRuleModel(self.database, pk).delete() super().delete() - def dump(self): # -> List[Dict]: + def dump(self) -> List[Dict]: config_rules = [] for a in self.config_rule: asdf = a.dump() @@ -62,7 +62,7 @@ class ConfigModel(Base): # pylint: disable=abstract-method class ConfigRuleModel(Base): # pylint: disable=abstract-method __tablename__ = 'ConfigRule' config_rule_uuid = Column(UUID(as_uuid=False), primary_key=True) - config_uuid = Column(UUID(as_uuid=False), ForeignKey("Config.config_uuid"), primary_key=True) + config_uuid = Column(UUID(as_uuid=False), ForeignKey("Config.config_uuid", ondelete='CASCADE'), primary_key=True) action = Column(Enum(ORM_ConfigActionEnum, create_constraint=True, native_enum=True), nullable=False) position = Column(INTEGER, nullable=False) diff --git a/src/context/service/database/ContextModel.py b/src/context/service/database/ContextModel.py index ef1d485be..cde774fe4 100644 --- a/src/context/service/database/ContextModel.py +++ b/src/context/service/database/ContextModel.py @@ -33,7 +33,8 @@ class ContextModel(Base): def dump_id(self) -> Dict: return {'context_uuid': {'uuid': self.context_uuid}} - def main_pk_name(self): + @staticmethod + def main_pk_name(): return 'context_uuid' """ diff --git a/src/context/service/database/DeviceModel.py b/src/context/service/database/DeviceModel.py index bf8f73c79..122da50af 100644 --- a/src/context/service/database/DeviceModel.py +++ b/src/context/service/database/DeviceModel.py @@ -49,14 +49,16 @@ class DeviceModel(Base): __tablename__ = 'Device' device_uuid = Column(UUID(as_uuid=False), primary_key=True) device_type = Column(String) - device_config_uuid = Column(UUID(as_uuid=False), ForeignKey("Config.config_uuid")) + device_config_uuid = Column(UUID(as_uuid=False), ForeignKey("Config.config_uuid", ondelete='CASCADE')) device_operational_status = Column(Enum(ORM_DeviceOperationalStatusEnum, create_constraint=False, native_enum=False)) # Relationships - device_config = relationship("ConfigModel", lazy="joined") - driver = relationship("DriverModel", lazy="joined") - endpoints = relationship("EndPointModel", lazy="joined") + device_config = relationship("ConfigModel", passive_deletes="all, delete", lazy="joined") + driver = relationship("DriverModel", passive_deletes=True, back_populates="device") + endpoints = relationship("EndPointModel", passive_deletes=True, back_populates="device") + + # topology = relationship("TopologyModel", lazy="joined") # def delete(self) -> None: # # pylint: disable=import-outside-toplevel @@ -83,13 +85,25 @@ class DeviceModel(Base): return self.device_config.dump() def dump_drivers(self) -> List[int]: - return self.driver.dump() + response = [] + + for a in self.driver: + LOGGER.info('DUMPPPPPPPPPPPPPPPPPPPPPIIIIIIIIIIIIIIIIIIIIIIINNNNNNNNNNNNNNNGGGGGGGGGGGGGGGGGGg') + LOGGER.info('aasdfadsf: {}'.format(a.dump())) + response.append(a.dump()) + + return response def dump_endpoints(self) -> List[Dict]: - return self.endpoints.dump() + response = [] + + for a in self.endpoints: + response.append(a.dump()) + + return response def dump( # pylint: disable=arguments-differ - self, include_config_rules=True, include_drivers=False, include_endpoints=False + self, include_config_rules=True, include_drivers=True, include_endpoints=True ) -> Dict: result = { 'device_id': self.dump_id(), @@ -101,24 +115,26 @@ class DeviceModel(Base): if include_endpoints: result['device_endpoints'] = self.dump_endpoints() return result - def main_pk_name(self): + @staticmethod + def main_pk_name(): return 'device_uuid' class DriverModel(Base): # pylint: disable=abstract-method __tablename__ = 'Driver' - driver_uuid = Column(UUID(as_uuid=False), primary_key=True) - device_uuid = Column(UUID(as_uuid=False), ForeignKey("Device.device_uuid"), primary_key=True) + # driver_uuid = Column(UUID(as_uuid=False), primary_key=True) + device_uuid = Column(UUID(as_uuid=False), ForeignKey("Device.device_uuid", ondelete='CASCADE'), primary_key=True) driver = Column(Enum(ORM_DeviceDriverEnum, create_constraint=False, native_enum=False)) # Relationships - device = relationship("DeviceModel") + device = relationship("DeviceModel", back_populates="driver") def dump(self) -> Dict: return self.driver.value - def main_pk_name(self): - return 'driver_uuid' + @staticmethod + def main_pk_name(): + return 'device_uuid' def set_drivers(database : Database, db_device : DeviceModel, grpc_device_drivers): db_device_pk = db_device.device_uuid diff --git a/src/context/service/database/EndPointModel.py b/src/context/service/database/EndPointModel.py index 669b590e3..a4381a2e3 100644 --- a/src/context/service/database/EndPointModel.py +++ b/src/context/service/database/EndPointModel.py @@ -27,14 +27,17 @@ LOGGER = logging.getLogger(__name__) class EndPointModel(Base): __tablename__ = 'EndPoint' - endpoint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) topology_uuid = Column(UUID(as_uuid=False), ForeignKey("Topology.topology_uuid"), primary_key=True) - device_uuid = Column(UUID(as_uuid=False), ForeignKey("Device.device_uuid"), primary_key=True) + device_uuid = Column(UUID(as_uuid=False), ForeignKey("Device.device_uuid", ondelete='CASCADE'), primary_key=True) + endpoint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) endpoint_type = Column(String) # Relationships + kpi_sample_types = relationship("KpiSampleTypeModel", passive_deletes=True, back_populates="EndPoint") + device = relationship("DeviceModel", back_populates="endpoints") - def main_pk_name(self): + @staticmethod + def main_pk_name(): return 'endpoint_uuid' def delete(self) -> None: @@ -44,32 +47,41 @@ class EndPointModel(Base): def dump_id(self) -> Dict: result = { - 'device_uuid': self.device_uuid, + 'device_id': self.device.dump_id(), 'endpoint_uuid': {'uuid': self.endpoint_uuid}, } return result def dump_kpi_sample_types(self) -> List[int]: - db_kpi_sample_type_pks = self.references(KpiSampleTypeModel) - return [KpiSampleTypeModel(self.database, pk).dump() for pk,_ in db_kpi_sample_type_pks] + # db_kpi_sample_type_pks = self.references(KpiSampleTypeModel) + # return [KpiSampleTypeModel(self.database, pk).dump() for pk,_ in db_kpi_sample_type_pks] + response = [] + for a in self.kpi_sample_types: + response.append(a.dump()) + return response def dump( # pylint: disable=arguments-differ self, include_kpi_sample_types=True ) -> Dict: result = { - 'endpoint_uuid': self.dump_id(), + 'endpoint_id': self.dump_id(), 'endpoint_type': self.endpoint_type, } if include_kpi_sample_types: result['kpi_sample_types'] = self.dump_kpi_sample_types() return result + class KpiSampleTypeModel(Base): # pylint: disable=abstract-method __tablename__ = 'KpiSampleType' kpi_uuid = Column(UUID(as_uuid=False), primary_key=True) - endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid")) + endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid", ondelete='CASCADE')) kpi_sample_type = Column(Enum(ORM_KpiSampleTypeEnum, create_constraint=False, native_enum=False)) # __table_args__ = (ForeignKeyConstraint([endpoint_uuid], [EndPointModel.endpoint_uuid]), {}) + + # Relationships + EndPoint = relationship("EndPointModel", passive_deletes=True, back_populates="kpi_sample_types") + def dump(self) -> Dict: return self.kpi_sample_type.value diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py index d104d5567..108ab9950 100644 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py @@ -46,7 +46,6 @@ from context.service.database.ConnectionModel import ConnectionModel, set_path from context.service.database.ConstraintModel import set_constraints from context.service.database.EndPointModel import EndPointModel, set_kpi_sample_types from context.service.database.Events import notify_event -from context.service.database.LinkModel import LinkModel from context.service.database.RelationModels import ( ConnectionSubServiceModel, LinkEndPointModel, ServiceEndPointModel, SliceEndPointModel, SliceServiceModel, SliceSubSliceModel, TopologyDeviceModel, TopologyLinkModel) @@ -60,6 +59,7 @@ from context.service.database.TopologyModel import TopologyModel from context.service.database.Events import notify_event from context.service.database.EndPointModel import EndPointModel from context.service.database.EndPointModel import KpiSampleTypeModel +from context.service.database.LinkModel import LinkModel from .Constants import ( CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE, @@ -268,7 +268,7 @@ class ContextServiceServicerImpl(ContextServiceServicer): def ListDevices(self, request: Empty, context : grpc.ServicerContext) -> DeviceList: with self.session() as session: result = session.query(DeviceModel).all() - return DeviceList(devices=[device.dump_id() for device in result]) + return DeviceList(devices=[device.dump() for device in result]) @safe_and_metered_rpc_method(METRICS, LOGGER) def GetDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Device: @@ -278,72 +278,76 @@ class ContextServiceServicerImpl(ContextServiceServicer): if not result: raise NotFoundException(DeviceModel.__name__.replace('Model', ''), device_uuid) - rd = result.dump() + rd = result.dump(include_config_rules=True, include_drivers=True, include_endpoints=True) + rt = Device(**rd) return rt @safe_and_metered_rpc_method(METRICS, LOGGER) def SetDevice(self, request: Device, context : grpc.ServicerContext) -> DeviceId: - device_uuid = request.device_id.device_uuid.uuid + with self.session() as session: + device_uuid = request.device_id.device_uuid.uuid - for i,endpoint in enumerate(request.device_endpoints): - endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid - if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid - if device_uuid != endpoint_device_uuid: - raise InvalidArgumentException( - 'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid, - ['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)]) - - config_rules = grpc_config_rules_to_raw(request.device_config.config_rules) - running_config_result = self.update_config(device_uuid, 'running', config_rules) - db_running_config = running_config_result[0][0] - config_uuid = db_running_config.config_uuid - - new_obj = DeviceModel(**{ - 'device_uuid' : device_uuid, - 'device_type' : request.device_type, - 'device_operational_status' : grpc_to_enum__device_operational_status(request.device_operational_status), - 'device_config_uuid' : config_uuid, - }) - result: Tuple[DeviceModel, bool] = self.database.create_or_update(new_obj) - db_device, updated = result + for i,endpoint in enumerate(request.device_endpoints): + endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid + if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid + if device_uuid != endpoint_device_uuid: + raise InvalidArgumentException( + 'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid, + ['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)]) - self.set_drivers(db_device, request.device_drivers) + config_rules = grpc_config_rules_to_raw(request.device_config.config_rules) + running_config_result = self.update_config(session, device_uuid, 'running', config_rules) + db_running_config = running_config_result[0][0] + config_uuid = db_running_config.config_uuid - for i,endpoint in enumerate(request.device_endpoints): - endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid - endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid - if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid + new_obj = DeviceModel(**{ + 'device_uuid' : device_uuid, + 'device_type' : request.device_type, + 'device_operational_status' : grpc_to_enum__device_operational_status(request.device_operational_status), + 'device_config_uuid' : config_uuid, + }) + result: Tuple[DeviceModel, bool] = self.database.create_or_update(new_obj) + db_device, updated = result - str_endpoint_key = key_to_str([device_uuid, endpoint_uuid]) - endpoint_attributes = { - 'device_uuid' : db_device.device_uuid, - 'endpoint_uuid': endpoint_uuid, - 'endpoint_type': endpoint.endpoint_type, - } + self.set_drivers(db_device, request.device_drivers) - endpoint_topology_context_uuid = endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid - endpoint_topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid - if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: - str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) + for i,endpoint in enumerate(request.device_endpoints): + endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid + endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid + if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid + + str_endpoint_key = key_to_str([device_uuid, endpoint_uuid]) + endpoint_attributes = { + 'device_uuid' : db_device.device_uuid, + 'endpoint_uuid': endpoint_uuid, + 'endpoint_type': endpoint.endpoint_type, + } + + endpoint_topology_context_uuid = endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid + endpoint_topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid + if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: + str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) - db_topology : TopologyModel = self.database.get_object(TopologyModel, endpoint_topology_uuid) + db_topology: TopologyModel = self.database.get_object(TopologyModel, endpoint_topology_uuid) + new_topo = TopologyModel(context_uuid=db_topology.context_uuid, topology_uuid=db_topology.topology_uuid, device_uuids=db_device.device_uuid) - str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') - endpoint_attributes['topology_uuid'] = db_topology.topology_uuid + self.database.create_or_update(new_topo) - new_endpoint = EndPointModel(**endpoint_attributes) - result : Tuple[EndPointModel, bool] = self.database.create_or_update(new_endpoint) - db_endpoint, updated = result + endpoint_attributes['topology_uuid'] = db_topology.topology_uuid - self.set_kpi_sample_types(db_endpoint, endpoint.kpi_sample_types) + new_endpoint = EndPointModel(**endpoint_attributes) + result : Tuple[EndPointModel, bool] = self.database.create_or_update(new_endpoint) + db_endpoint, updated = result - # event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_device_id = db_device.dump_id() - # notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id}) + self.set_kpi_sample_types(db_endpoint, endpoint.kpi_sample_types) - return DeviceId(**dict_device_id) + # event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE + dict_device_id = db_device.dump_id() + # notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id}) + + return DeviceId(**dict_device_id) def set_kpi_sample_types(self, db_endpoint: EndPointModel, grpc_endpoint_kpi_sample_types): db_endpoint_pk = db_endpoint.endpoint_uuid @@ -362,7 +366,7 @@ class ContextServiceServicerImpl(ContextServiceServicer): orm_driver = grpc_to_enum__device_driver(driver) str_device_driver_key = key_to_str([db_device_pk, orm_driver.name]) driver_config = { - "driver_uuid": str(uuid.uuid4()), + # "driver_uuid": str(uuid.uuid4()), "device_uuid": db_device_pk, "driver": orm_driver.name } @@ -373,13 +377,19 @@ class ContextServiceServicerImpl(ContextServiceServicer): self.database.create_or_update(db_device_driver) def update_config( - self, db_parent_pk: str, config_name: str, + self, session, db_parent_pk: str, config_name: str, raw_config_rules: List[Tuple[ORM_ConfigActionEnum, str, str]] ) -> List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]]: - str_config_key = key_to_str([db_parent_pk, config_name], separator=':') - result = self.database.get_or_create(ConfigModel, db_parent_pk) - db_config, created = result + created = False + + db_config = session.query(ConfigModel).filter_by(**{ConfigModel.main_pk_name(): db_parent_pk}).one_or_none() + if not db_config: + db_config = ConfigModel() + setattr(db_config, ConfigModel.main_pk_name(), db_parent_pk) + session.add(db_config) + session.commit() + created = True LOGGER.info('UPDATED-CONFIG: {}'.format(db_config.dump())) @@ -452,15 +462,16 @@ class ContextServiceServicerImpl(ContextServiceServicer): @safe_and_metered_rpc_method(METRICS, LOGGER) def RemoveDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Empty: - with self.lock: - device_uuid = request.device_uuid.uuid - db_device = DeviceModel(self.database, device_uuid, auto_load=False) - found = db_device.load() - if not found: return Empty() + device_uuid = request.device_uuid.uuid - dict_device_id = db_device.dump_id() - db_device.delete() + with self.session() as session: + result = session.query(DeviceModel).filter_by(device_uuid=device_uuid).one_or_none() + if not result: + return Empty() + dict_device_id = result.dump_id() + session.query(DeviceModel).filter_by(device_uuid=device_uuid).delete() + session.commit() event_type = EventTypeEnum.EVENTTYPE_REMOVE notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id}) return Empty() @@ -472,75 +483,86 @@ class ContextServiceServicerImpl(ContextServiceServicer): - """ # ----- Link ------------------------------------------------------------------------------------------------------- @safe_and_metered_rpc_method(METRICS, LOGGER) def ListLinkIds(self, request: Empty, context : grpc.ServicerContext) -> LinkIdList: - with self.lock: - db_links : List[LinkModel] = get_all_objects(self.database, LinkModel) - db_links = sorted(db_links, key=operator.attrgetter('pk')) - return LinkIdList(link_ids=[db_link.dump_id() for db_link in db_links]) + with self.session() as session: + result = session.query(LinkModel).all() + return LinkIdList(link_ids=[db_link.dump_id() for db_link in result]) + @safe_and_metered_rpc_method(METRICS, LOGGER) def ListLinks(self, request: Empty, context : grpc.ServicerContext) -> LinkList: - with self.lock: - db_links : List[LinkModel] = get_all_objects(self.database, LinkModel) - db_links = sorted(db_links, key=operator.attrgetter('pk')) - return LinkList(links=[db_link.dump() for db_link in db_links]) + with self.session() as session: + result = session.query(DeviceModel).all() + return LinkList(links=[db_link.dump() for db_link in result]) @safe_and_metered_rpc_method(METRICS, LOGGER) def GetLink(self, request: LinkId, context : grpc.ServicerContext) -> Link: - with self.lock: - link_uuid = request.link_uuid.uuid - db_link : LinkModel = get_object(self.database, LinkModel, link_uuid) - return Link(**db_link.dump()) + link_uuid = request.link_uuid.uuid + with self.session() as session: + result = session.query(LinkModel).filter(LinkModel.device_uuid == link_uuid).one_or_none() + if not result: + raise NotFoundException(DeviceModel.__name__.replace('Model', ''), link_uuid) - @safe_and_metered_rpc_method(METRICS, LOGGER) - def SetLink(self, request: Link, context : grpc.ServicerContext) -> LinkId: - with self.lock: - link_uuid = request.link_id.link_uuid.uuid - result : Tuple[LinkModel, bool] = update_or_create_object( - self.database, LinkModel, link_uuid, {'link_uuid': link_uuid}) - db_link, updated = result + rd = result.dump() - for endpoint_id in request.link_endpoint_ids: - endpoint_uuid = endpoint_id.endpoint_uuid.uuid - endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid - endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid - endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid + rt = Link(**rd) - str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) + return rt - db_topology = None - if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: - str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) - db_topology : TopologyModel = get_object(self.database, TopologyModel, str_topology_key) - str_topology_device_key = key_to_str([str_topology_key, endpoint_device_uuid], separator='--') - # check device is in topology - get_object(self.database, TopologyDeviceModel, str_topology_device_key) - str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') - db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) - str_link_endpoint_key = key_to_str([link_uuid, endpoint_device_uuid], separator='--') - result : Tuple[LinkEndPointModel, bool] = get_or_create_object( - self.database, LinkEndPointModel, str_link_endpoint_key, { - 'link_fk': db_link, 'endpoint_fk': db_endpoint}) - #db_link_endpoint, link_endpoint_created = result + @safe_and_metered_rpc_method(METRICS, LOGGER) + def SetLink(self, request: Link, context : grpc.ServicerContext) -> LinkId: + link_uuid = request.link_id.link_uuid.uuid - if db_topology is not None: - str_topology_link_key = key_to_str([str_topology_key, link_uuid], separator='--') - result : Tuple[TopologyLinkModel, bool] = get_or_create_object( - self.database, TopologyLinkModel, str_topology_link_key, { - 'topology_fk': db_topology, 'link_fk': db_link}) - #db_topology_link, topology_link_created = result + new_link = LinkModel(**{ + 'lin_uuid': link_uuid + }) + result: Tuple[LinkModel, bool] = self.database.create_or_update(new_link) + db_link, updated = result - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_link_id = db_link.dump_id() - notify_event(self.messagebroker, TOPIC_LINK, event_type, {'link_id': dict_link_id}) - return LinkId(**dict_link_id) + for endpoint_id in request.link_endpoint_ids: + endpoint_uuid = endpoint_id.endpoint_uuid.uuid + endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid + endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid + endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid + + str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) + + if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: + str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) + # db_topology : TopologyModel = get_object(self.database, TopologyModel, str_topology_key) + db_topology : TopologyModel = self.database.get_object(TopologyModel, str_topology_key) + str_topology_device_key = key_to_str([str_topology_key, endpoint_device_uuid], separator='--') + # check device is in topology + # get_object(self.database, TopologyDeviceModel, str_topology_device_key) + # str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') + + # db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) + LOGGER.info('str_endpoint_key: {}'.format(str_endpoint_key)) + db_endpoint: EndPointModel = self.database.get_object(EndPointModel, str_endpoint_key) + + # str_link_endpoint_key = key_to_str([link_uuid, endpoint_device_uuid], separator='--') + # result : Tuple[LinkEndPointModel, bool] = get_or_create_object( + # self.database, LinkEndPointModel, str_link_endpoint_key, { + # 'link_fk': db_link, 'endpoint_fk': db_endpoint}) + #db_link_endpoint, link_endpoint_created = result + + # if db_topology is not None: + # str_topology_link_key = key_to_str([str_topology_key, link_uuid], separator='--') + # result : Tuple[TopologyLinkModel, bool] = get_or_create_object( + # self.database, TopologyLinkModel, str_topology_link_key, { + # 'topology_fk': db_topology, 'link_fk': db_link}) + # #db_topology_link, topology_link_created = result + + event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE + dict_link_id = db_link.dump_id() + notify_event(self.messagebroker, TOPIC_LINK, event_type, {'link_id': dict_link_id}) + return LinkId(**dict_link_id) @safe_and_metered_rpc_method(METRICS, LOGGER) def RemoveLink(self, request: LinkId, context : grpc.ServicerContext) -> Empty: @@ -562,6 +584,7 @@ class ContextServiceServicerImpl(ContextServiceServicer): for message in self.messagebroker.consume({TOPIC_LINK}, consume_timeout=CONSUME_TIMEOUT): yield LinkEvent(**json.loads(message.content)) + """ # ----- Service ---------------------------------------------------------------------------------------------------- -- GitLab From a56a5557747a9afbaad375e08511d0cd07996d47 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 2 Dec 2022 16:58:18 +0000 Subject: [PATCH 072/353] Common - Method Wrappers: - updated default decorator histogram buckets - updated grafana dashboard pannel backups - updated testing deploy script --- src/common/method_wrappers/Decorator.py | 10 +- .../method_wrappers/tests/deploy_specs.sh | 4 +- .../grafana_prometheus_component_rpc.json | 228 +++++++++------- .../grafana_prometheus_device_driver.json | 237 ++++++++++------- .../grafana_prometheus_service_handler.json | 244 ++++++++++-------- 5 files changed, 415 insertions(+), 308 deletions(-) diff --git a/src/common/method_wrappers/Decorator.py b/src/common/method_wrappers/Decorator.py index c7ddfa231..01c256ff6 100644 --- a/src/common/method_wrappers/Decorator.py +++ b/src/common/method_wrappers/Decorator.py @@ -33,11 +33,11 @@ METRIC_TO_CLASS_PARAMS = { MetricTypeEnum.HISTOGRAM_DURATION: (Histogram, { 'buckets': ( # .005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, INF - 0.001, 0.002, 0.003, 0.004, 0.005, 0.0075, - 0.010, 0.025, 0.050, 0.075, - 0.100, 0.250, 0.500, 0.750, - 1.000, 2.500, 5.000, 7.500, - 10.00, INF) + 0.0010, 0.0025, 0.0050, 0.0075, + 0.0100, 0.0250, 0.0500, 0.0750, + 0.1000, 0.2500, 0.5000, 0.7500, + 1.0000, 2.5000, 5.0000, 7.5000, + INF) }) } diff --git a/src/common/method_wrappers/tests/deploy_specs.sh b/src/common/method_wrappers/tests/deploy_specs.sh index 3e4883251..238918480 100644 --- a/src/common/method_wrappers/tests/deploy_specs.sh +++ b/src/common/method_wrappers/tests/deploy_specs.sh @@ -7,7 +7,7 @@ export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" # interdomain slice pathcomp dlt # dbscanserving opticalattackmitigator opticalattackdetector # l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector -export TFS_COMPONENTS="context device monitoring pathcomp service slice compute webui" # automation +export TFS_COMPONENTS="context device pathcomp service slice webui" # automation monitoring compute # Set the tag you want to use for your images. export TFS_IMAGE_TAG="dev" @@ -16,7 +16,7 @@ export TFS_IMAGE_TAG="dev" export TFS_K8S_NAMESPACE="tfs" # Set additional manifest files to be applied after the deployment -export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml src/common/method_wrappers/tests/servicemonitors.yaml" +export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml manifests/servicemonitors.yaml" # Set the new Grafana admin password export TFS_GRAFANA_PASSWORD="admin123+" diff --git a/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json b/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json index 33aba15b6..ba088252a 100644 --- a/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json +++ b/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json @@ -16,7 +16,7 @@ "gnetId": null, "graphTooltip": 0, "id": 27, - "iteration": 1669834289148, + "iteration": 1669995219512, "links": [], "panels": [ { @@ -40,11 +40,13 @@ "hiddenSeries": false, "id": 4, "legend": { + "alignAsTable": false, "avg": false, "current": false, "max": false, "min": false, - "show": true, + "rightSide": false, + "show": false, "total": false, "values": false }, @@ -66,26 +68,26 @@ "targets": [ { "exemplar": true, - "expr": "tfs_[[component]]_rpc_[[method]]_counter_requests_started_total", + "expr": "sum(tfs_[[component]]_rpc_[[method]]_counter_requests_started_total{pod=~\"[[pod]]\"})", "interval": "", - "legendFormat": "{{__name__}}", + "legendFormat": "started", "queryType": "randomWalk", "refId": "A" }, { "exemplar": true, - "expr": "tfs_[[component]]_rpc_[[method]]_counter_requests_completed_total", + "expr": "sum(tfs_[[component]]_rpc_[[method]]_counter_requests_completed_total{pod=~\"[[pod]]\"})", "hide": false, "interval": "", - "legendFormat": "{{__name__}}", + "legendFormat": "completed", "refId": "B" }, { "exemplar": true, - "expr": "tfs_[[component]]_rpc_[[method]]_counter_requests_failed_total", + "expr": "sum(tfs_[[component]]_rpc_[[method]]_counter_requests_started_total{pod=~\"[[pod]]\"})", "hide": false, "interval": "", - "legendFormat": "{{__name__}}", + "legendFormat": "failed", "refId": "C" } ], @@ -99,15 +101,7 @@ "sort": 0, "value_type": "individual" }, - "transformations": [ - { - "id": "renameByRegex", - "options": { - "regex": "service_.*_counter_requests_(.*)_total", - "renamePattern": "$1" - } - } - ], + "transformations": [], "type": "graph", "xaxis": { "buckets": null, @@ -141,6 +135,79 @@ "alignLevel": null } }, + { + "cards": { + "cardPadding": null, + "cardRound": null + }, + "color": { + "cardColor": "#b4ff00", + "colorScale": "linear", + "colorScheme": "interpolateRdYlGn", + "exponent": 0.5, + "max": null, + "min": 0, + "mode": "opacity" + }, + "dataFormat": "tsbuckets", + "datasource": "prometheus", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 6 + }, + "heatmap": {}, + "hideZeroBuckets": true, + "highlightCards": true, + "id": 2, + "interval": "60s", + "legend": { + "show": true + }, + "pluginVersion": "7.5.4", + "reverseYBuckets": false, + "targets": [ + { + "exemplar": true, + "expr": "sum(increase(tfs_[[component]]_rpc_[[method]]_histogram_duration_bucket{pod=~\"[[pod]]\"}[$__rate_interval])) by (le)", + "format": "heatmap", + "instant": false, + "interval": "10s", + "intervalFactor": 1, + "legendFormat": "{{le}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "title": "Histogram", + "tooltip": { + "show": true, + "showHistogram": true + }, + "type": "heatmap", + "xAxis": { + "show": true + }, + "xBucketNumber": null, + "xBucketSize": null, + "yAxis": { + "decimals": null, + "format": "s", + "logBase": 1, + "max": null, + "min": null, + "show": true, + "splitFactor": null + }, + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null + }, { "aliasColors": {}, "bars": false, @@ -157,15 +224,17 @@ "h": 6, "w": 24, "x": 0, - "y": 6 + "y": 14 }, "hiddenSeries": false, "id": 5, "legend": { + "alignAsTable": false, "avg": false, "current": false, "max": false, "min": false, + "rightSide": false, "show": false, "total": false, "values": false @@ -188,7 +257,7 @@ "targets": [ { "exemplar": true, - "expr": "tfs_[[component]]_rpc_[[method]]_histogram_duration_sum", + "expr": "sum(tfs_[[component]]_rpc_[[method]]_histogram_duration_sum{pod=~\"[[pod]]\"})", "hide": false, "interval": "", "legendFormat": "total time", @@ -238,81 +307,9 @@ "align": false, "alignLevel": null } - }, - { - "cards": { - "cardPadding": null, - "cardRound": null - }, - "color": { - "cardColor": "#b4ff00", - "colorScale": "sqrt", - "colorScheme": "interpolateRdYlGn", - "exponent": 0.5, - "max": null, - "min": 0, - "mode": "opacity" - }, - "dataFormat": "tsbuckets", - "datasource": "prometheus", - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 12 - }, - "heatmap": {}, - "hideZeroBuckets": true, - "highlightCards": true, - "id": 2, - "legend": { - "show": false - }, - "pluginVersion": "7.5.4", - "reverseYBuckets": false, - "targets": [ - { - "exemplar": true, - "expr": "sum(increase(tfs_[[component]]_rpc_[[method]]_histogram_duration_bucket[$__rate_interval])) by (le)", - "format": "heatmap", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{le}}", - "queryType": "randomWalk", - "refId": "A" - } - ], - "title": "Histogram", - "tooltip": { - "show": true, - "showHistogram": true - }, - "type": "heatmap", - "xAxis": { - "show": true - }, - "xBucketNumber": null, - "xBucketSize": null, - "yAxis": { - "decimals": null, - "format": "s", - "logBase": 1, - "max": null, - "min": null, - "show": true, - "splitFactor": null - }, - "yBucketBound": "auto", - "yBucketNumber": null, - "yBucketSize": null } ], - "refresh": false, + "refresh": "5s", "schemaVersion": 27, "style": "dark", "tags": [], @@ -322,8 +319,8 @@ "allValue": null, "current": { "selected": false, - "text": "device", - "value": "device" + "text": "context", + "value": "context" }, "datasource": "prometheus", "definition": "metrics(tfs_)", @@ -350,11 +347,11 @@ "useTags": false }, { - "allValue": null, + "allValue": "", "current": { "selected": false, - "text": "adddevice", - "value": "adddevice" + "text": "getcontext", + "value": "getcontext" }, "datasource": "prometheus", "definition": "metrics(tfs_[[component]]_rpc_)", @@ -379,6 +376,41 @@ "tagsQuery": "", "type": "query", "useTags": false + }, + { + "allValue": ".*", + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": "prometheus", + "definition": "label_values(tfs_[[component]]_rpc_[[method]]_histogram_duration_bucket, pod)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Pod", + "multi": true, + "name": "pod", + "options": [], + "query": { + "query": "label_values(tfs_[[component]]_rpc_[[method]]_histogram_duration_bucket, pod)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false } ] }, @@ -390,5 +422,5 @@ "timezone": "", "title": "TFS / Component RPCs", "uid": "KKxzxIFVz", - "version": 7 + "version": 21 } \ No newline at end of file diff --git a/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json b/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json index 629cf0140..a8663239b 100644 --- a/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json +++ b/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json @@ -16,7 +16,7 @@ "gnetId": null, "graphTooltip": 0, "id": 25, - "iteration": 1669834795409, + "iteration": 1669993886467, "links": [], "panels": [ { @@ -40,11 +40,13 @@ "hiddenSeries": false, "id": 4, "legend": { + "alignAsTable": false, "avg": false, "current": false, "max": false, "min": false, - "show": true, + "rightSide": false, + "show": false, "total": false, "values": false }, @@ -66,26 +68,26 @@ "targets": [ { "exemplar": true, - "expr": "tfs_device_driver_[[method]]_counter_requests_started_total{driver=\"[[driver]]\"}", + "expr": "sum(tfs_device_driver_[[method]]_counter_requests_started_total{driver=~\"[[driver]]\", pod=~\"deviceservice-[[pod]]\"})", "interval": "", - "legendFormat": "{{__name__}}", + "legendFormat": "started", "queryType": "randomWalk", "refId": "A" }, { "exemplar": true, - "expr": "tfs_device_driver_[[method]]_counter_requests_completed_total{driver=\"[[driver]]\"}", + "expr": "sum(tfs_device_driver_[[method]]_counter_requests_completed_total{driver=~\"[[driver]]\", pod=~\"deviceservice-[[pod]]\"})", "hide": false, "interval": "", - "legendFormat": "{{__name__}}", + "legendFormat": "completed", "refId": "B" }, { "exemplar": true, - "expr": "tfs_device_driver_[[method]]_counter_requests_failed_total{driver=\"[[driver]]\"}", + "expr": "sum(tfs_device_driver_[[method]]_counter_requests_failed_total{driver=~\"[[driver]]\", pod=~\"deviceservice-[[pod]]\"})", "hide": false, "interval": "", - "legendFormat": "{{__name__}}", + "legendFormat": "failed", "refId": "C" } ], @@ -99,15 +101,7 @@ "sort": 0, "value_type": "individual" }, - "transformations": [ - { - "id": "renameByRegex", - "options": { - "regex": "tfs_device_driver_.*_counter_requests_(.*)_total", - "renamePattern": "$1" - } - } - ], + "transformations": [], "type": "graph", "xaxis": { "buckets": null, @@ -141,6 +135,80 @@ "alignLevel": null } }, + { + "cards": { + "cardPadding": null, + "cardRound": null + }, + "color": { + "cardColor": "#b4ff00", + "colorScale": "linear", + "colorScheme": "interpolateRdYlGn", + "exponent": 0.5, + "max": null, + "min": 0, + "mode": "opacity" + }, + "dataFormat": "tsbuckets", + "datasource": "prometheus", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 6 + }, + "heatmap": {}, + "hideZeroBuckets": true, + "highlightCards": true, + "id": 2, + "interval": "60s", + "legend": { + "show": true + }, + "pluginVersion": "7.5.4", + "reverseYBuckets": false, + "targets": [ + { + "exemplar": true, + "expr": "sum(increase(tfs_device_driver_[[method]]_histogram_duration_bucket{driver=~\"[[driver]]\", pod=~\"deviceservice-[[pod]]\"}[$__rate_interval])) by (le)", + "format": "heatmap", + "instant": false, + "interval": "10s", + "intervalFactor": 1, + "legendFormat": "{{le}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "timeFrom": null, + "title": "Histogram", + "tooltip": { + "show": true, + "showHistogram": true + }, + "type": "heatmap", + "xAxis": { + "show": true + }, + "xBucketNumber": null, + "xBucketSize": null, + "yAxis": { + "decimals": null, + "format": "s", + "logBase": 1, + "max": null, + "min": null, + "show": true, + "splitFactor": null + }, + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null + }, { "aliasColors": {}, "bars": false, @@ -157,15 +225,17 @@ "h": 6, "w": 24, "x": 0, - "y": 6 + "y": 14 }, "hiddenSeries": false, "id": 5, "legend": { + "alignAsTable": false, "avg": false, "current": false, "max": false, "min": false, + "rightSide": false, "show": false, "total": false, "values": false @@ -188,7 +258,7 @@ "targets": [ { "exemplar": true, - "expr": "tfs_device_driver_[[method]]_histogram_duration_sum{driver=\"[[driver]]\"}", + "expr": "sum(tfs_device_driver_[[method]]_histogram_duration_sum{driver=~\"[[driver]]\", pod=~\"deviceservice-[[pod]]\"})", "hide": false, "interval": "", "legendFormat": "total time", @@ -238,78 +308,6 @@ "align": false, "alignLevel": null } - }, - { - "cards": { - "cardPadding": null, - "cardRound": null - }, - "color": { - "cardColor": "#b4ff00", - "colorScale": "sqrt", - "colorScheme": "interpolateRdYlGn", - "exponent": 0.5, - "max": null, - "min": 0, - "mode": "opacity" - }, - "dataFormat": "tsbuckets", - "datasource": "prometheus", - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 12 - }, - "heatmap": {}, - "hideZeroBuckets": true, - "highlightCards": true, - "id": 2, - "legend": { - "show": false - }, - "pluginVersion": "7.5.4", - "reverseYBuckets": false, - "targets": [ - { - "exemplar": true, - "expr": "sum(increase(tfs_device_driver_[[method]]_histogram_duration_bucket{driver=\"[[driver]]\"}[$__rate_interval])) by (le)", - "format": "heatmap", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{le}}", - "queryType": "randomWalk", - "refId": "A" - } - ], - "title": "Histogram", - "tooltip": { - "show": true, - "showHistogram": true - }, - "type": "heatmap", - "xAxis": { - "show": true - }, - "xBucketNumber": null, - "xBucketSize": null, - "yAxis": { - "decimals": null, - "format": "s", - "logBase": 1, - "max": null, - "min": null, - "show": true, - "splitFactor": null - }, - "yBucketBound": "auto", - "yBucketNumber": null, - "yBucketSize": null } ], "refresh": "5s", @@ -319,7 +317,7 @@ "templating": { "list": [ { - "allValue": null, + "allValue": "", "current": { "selected": false, "text": "setconfig", @@ -350,20 +348,24 @@ "useTags": false }, { - "allValue": null, + "allValue": ".*", "current": { - "selected": false, - "text": "emulated", - "value": "emulated" + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] }, "datasource": "prometheus", "definition": "label_values(tfs_device_driver_[[method]]_histogram_duration_bucket, driver)", "description": null, "error": null, "hide": 0, - "includeAll": false, + "includeAll": true, "label": "Driver", - "multi": false, + "multi": true, "name": "driver", "options": [], "query": { @@ -379,16 +381,51 @@ "tagsQuery": "", "type": "query", "useTags": false + }, + { + "allValue": ".*", + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": "prometheus", + "definition": "label_values(tfs_device_driver_[[method]]_histogram_duration_bucket, pod)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Pod", + "multi": true, + "name": "pod", + "options": [], + "query": { + "query": "label_values(tfs_device_driver_[[method]]_histogram_duration_bucket, pod)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/deviceservice-(.*)/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false } ] }, "time": { - "from": "now-30m", + "from": "now-15m", "to": "now" }, "timepicker": {}, "timezone": "", "title": "TFS / Device / Driver", "uid": "eAg-wsOVk", - "version": 17 + "version": 30 } \ No newline at end of file diff --git a/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json b/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json index 299b01a7b..6f65a78b2 100644 --- a/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json +++ b/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json @@ -16,7 +16,7 @@ "gnetId": null, "graphTooltip": 0, "id": 26, - "iteration": 1669834937164, + "iteration": 1669992491077, "links": [], "panels": [ { @@ -40,11 +40,13 @@ "hiddenSeries": false, "id": 4, "legend": { + "alignAsTable": false, "avg": false, "current": false, "max": false, "min": false, - "show": true, + "rightSide": false, + "show": false, "total": false, "values": false }, @@ -66,26 +68,27 @@ "targets": [ { "exemplar": true, - "expr": "tfs_service_handler_[[method]]_counter_requests_started_total{handler=\"[[handler]]\"} ", + "expr": "sum(tfs_service_handler_[[method]]_counter_requests_started_total{handler=~\"[[handler]]\", pod=~\"serviceservice-[[pod]]\"})", + "instant": false, "interval": "", - "legendFormat": "{{__name__}}", + "legendFormat": "started", "queryType": "randomWalk", "refId": "A" }, { "exemplar": true, - "expr": "tfs_service_handler_[[method]]_counter_requests_completed_total{handler=\"[[handler]]\"}", + "expr": "sum(tfs_service_handler_[[method]]_counter_requests_completed_total{handler=~\"[[handler]]\", pod=~\"serviceservice-[[pod]]\"})", "hide": false, "interval": "", - "legendFormat": "{{__name__}}", + "legendFormat": "completed", "refId": "B" }, { "exemplar": true, - "expr": "tfs_service_handler_[[method]]_counter_requests_failed_total{handler=\"[[handler]]\"}", + "expr": "sum(tfs_service_handler_[[method]]_counter_requests_failed_total{handler=~\"[[handler]]\", pod=~\"serviceservice-[[pod]]\"})", "hide": false, "interval": "", - "legendFormat": "{{__name__}}", + "legendFormat": "failed", "refId": "C" } ], @@ -99,15 +102,7 @@ "sort": 0, "value_type": "individual" }, - "transformations": [ - { - "id": "renameByRegex", - "options": { - "regex": "tfs_service_handler_.*_counter_requests_(.*)_total", - "renamePattern": "$1" - } - } - ], + "transformations": [], "type": "graph", "xaxis": { "buckets": null, @@ -141,6 +136,80 @@ "alignLevel": null } }, + { + "cards": { + "cardPadding": null, + "cardRound": null + }, + "color": { + "cardColor": "#b4ff00", + "colorScale": "linear", + "colorScheme": "interpolateRdYlGn", + "exponent": 0.5, + "max": null, + "min": 0, + "mode": "opacity" + }, + "dataFormat": "tsbuckets", + "datasource": "prometheus", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 6 + }, + "heatmap": {}, + "hideZeroBuckets": true, + "highlightCards": true, + "id": 2, + "interval": "60s", + "legend": { + "show": true + }, + "pluginVersion": "7.5.4", + "reverseYBuckets": false, + "targets": [ + { + "exemplar": true, + "expr": "sum(increase(tfs_service_handler_[[method]]_histogram_duration_bucket{handler=~\"[[handler]]\", pod=~\"serviceservice-[[pod]]\"}[$__rate_interval])) by (le)", + "format": "heatmap", + "instant": false, + "interval": "10s", + "intervalFactor": 1, + "legendFormat": "{{le}}", + "queryType": "randomWalk", + "refId": "A" + } + ], + "timeFrom": null, + "title": "Histogram", + "tooltip": { + "show": true, + "showHistogram": true + }, + "type": "heatmap", + "xAxis": { + "show": true + }, + "xBucketNumber": null, + "xBucketSize": null, + "yAxis": { + "decimals": null, + "format": "s", + "logBase": 1, + "max": null, + "min": null, + "show": true, + "splitFactor": null + }, + "yBucketBound": "auto", + "yBucketNumber": null, + "yBucketSize": null + }, { "aliasColors": {}, "bars": false, @@ -157,15 +226,17 @@ "h": 6, "w": 24, "x": 0, - "y": 6 + "y": 14 }, "hiddenSeries": false, "id": 5, "legend": { + "alignAsTable": false, "avg": false, "current": false, "max": false, "min": false, + "rightSide": false, "show": false, "total": false, "values": false @@ -188,7 +259,7 @@ "targets": [ { "exemplar": true, - "expr": "tfs_service_handler_[[method]]_histogram_duration_sum{handler=\"[[handler]]\"}", + "expr": "sum(tfs_service_handler_[[method]]_histogram_duration_sum{handler=~\"[[handler]]\", pod=~\"serviceservice-[[pod]]\"})", "hide": false, "interval": "", "legendFormat": "total time", @@ -238,78 +309,6 @@ "align": false, "alignLevel": null } - }, - { - "cards": { - "cardPadding": null, - "cardRound": null - }, - "color": { - "cardColor": "#b4ff00", - "colorScale": "sqrt", - "colorScheme": "interpolateRdYlGn", - "exponent": 0.5, - "max": null, - "min": 0, - "mode": "opacity" - }, - "dataFormat": "tsbuckets", - "datasource": "prometheus", - "fieldConfig": { - "defaults": {}, - "overrides": [] - }, - "gridPos": { - "h": 8, - "w": 24, - "x": 0, - "y": 12 - }, - "heatmap": {}, - "hideZeroBuckets": true, - "highlightCards": true, - "id": 2, - "legend": { - "show": false - }, - "pluginVersion": "7.5.4", - "reverseYBuckets": false, - "targets": [ - { - "exemplar": true, - "expr": "sum(increase(tfs_service_handler_[[method]]_histogram_duration_bucket{handler=\"[[handler]]\"}[$__rate_interval])) by (le)", - "format": "heatmap", - "instant": false, - "interval": "", - "intervalFactor": 1, - "legendFormat": "{{le}}", - "queryType": "randomWalk", - "refId": "A" - } - ], - "title": "Histogram", - "tooltip": { - "show": true, - "showHistogram": true - }, - "type": "heatmap", - "xAxis": { - "show": true - }, - "xBucketNumber": null, - "xBucketSize": null, - "yAxis": { - "decimals": null, - "format": "s", - "logBase": 1, - "max": null, - "min": null, - "show": true, - "splitFactor": null - }, - "yBucketBound": "auto", - "yBucketNumber": null, - "yBucketSize": null } ], "refresh": "5s", @@ -319,11 +318,11 @@ "templating": { "list": [ { - "allValue": null, + "allValue": "", "current": { "selected": false, - "text": "setconfig", - "value": "setconfig" + "text": "setendpoint", + "value": "setendpoint" }, "datasource": "prometheus", "definition": "metrics(tfs_service_handler_.+)", @@ -340,7 +339,7 @@ "refId": "StandardVariableQuery" }, "refresh": 2, - "regex": "/tfs_service_handler_(.+config)_histogram_duration_bucket/", + "regex": "/tfs_service_handler_(.+)_histogram_duration_bucket/", "skipUrlSync": false, "sort": 0, "tagValuesQuery": "", @@ -350,20 +349,24 @@ "useTags": false }, { - "allValue": null, + "allValue": ".*", "current": { - "selected": false, - "text": "l2nm_emulated", - "value": "l2nm_emulated" + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] }, "datasource": "prometheus", "definition": "label_values(tfs_service_handler_[[method]]_histogram_duration_bucket, handler)", "description": null, "error": null, "hide": 0, - "includeAll": false, + "includeAll": true, "label": "Handler", - "multi": false, + "multi": true, "name": "handler", "options": [], "query": { @@ -379,16 +382,51 @@ "tagsQuery": "", "type": "query", "useTags": false + }, + { + "allValue": ".*", + "current": { + "selected": true, + "text": [ + "All" + ], + "value": [ + "$__all" + ] + }, + "datasource": "prometheus", + "definition": "label_values(tfs_service_handler_[[method]]_histogram_duration_bucket, pod)", + "description": null, + "error": null, + "hide": 0, + "includeAll": true, + "label": "Pod", + "multi": true, + "name": "pod", + "options": [], + "query": { + "query": "label_values(tfs_service_handler_[[method]]_histogram_duration_bucket, pod)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "/serviceservice-(.*)/", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false } ] }, "time": { - "from": "now-30m", + "from": "now-15m", "to": "now" }, "timepicker": {}, "timezone": "", "title": "TFS / Service / Handler", "uid": "DNOhOIF4k", - "version": 10 + "version": 16 } \ No newline at end of file -- GitLab From 788bb1257745908c04d01b4d47414dfcbc596d6b Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 2 Dec 2022 17:10:16 +0000 Subject: [PATCH 073/353] Manifests: - updated replicas of service and pathcomp to 5 - temporarily disabled WebUI Grafana --- manifests/pathcompservice.yaml | 1 + manifests/serviceservice.yaml | 2 +- manifests/webuiservice.yaml | 80 +++++++++++++++++----------------- 3 files changed, 42 insertions(+), 41 deletions(-) diff --git a/manifests/pathcompservice.yaml b/manifests/pathcompservice.yaml index 92e24ac42..51c83a0f3 100644 --- a/manifests/pathcompservice.yaml +++ b/manifests/pathcompservice.yaml @@ -20,6 +20,7 @@ spec: selector: matchLabels: app: pathcompservice + replicas: 5 template: metadata: labels: diff --git a/manifests/serviceservice.yaml b/manifests/serviceservice.yaml index d9ecc998e..089be20f9 100644 --- a/manifests/serviceservice.yaml +++ b/manifests/serviceservice.yaml @@ -20,7 +20,7 @@ spec: selector: matchLabels: app: serviceservice - replicas: 1 + replicas: 5 template: metadata: labels: diff --git a/manifests/webuiservice.yaml b/manifests/webuiservice.yaml index 7f70e837c..be400c691 100644 --- a/manifests/webuiservice.yaml +++ b/manifests/webuiservice.yaml @@ -60,43 +60,43 @@ spec: limits: cpu: 700m memory: 1024Mi - - name: grafana - image: grafana/grafana:8.5.11 - imagePullPolicy: IfNotPresent - ports: - - containerPort: 3000 - name: http-grafana - protocol: TCP - env: - - name: GF_SERVER_ROOT_URL - value: "http://0.0.0.0:3000/grafana/" - - name: GF_SERVER_SERVE_FROM_SUB_PATH - value: "true" - readinessProbe: - failureThreshold: 3 - httpGet: - path: /robots.txt - port: 3000 - scheme: HTTP - initialDelaySeconds: 10 - periodSeconds: 30 - successThreshold: 1 - timeoutSeconds: 2 - livenessProbe: - failureThreshold: 3 - initialDelaySeconds: 30 - periodSeconds: 10 - successThreshold: 1 - tcpSocket: - port: 3000 - timeoutSeconds: 1 - resources: - requests: - cpu: 250m - memory: 750Mi - limits: - cpu: 700m - memory: 1024Mi +# - name: grafana +# image: grafana/grafana:8.5.11 +# imagePullPolicy: IfNotPresent +# ports: +# - containerPort: 3000 +# name: http-grafana +# protocol: TCP +# env: +# - name: GF_SERVER_ROOT_URL +# value: "http://0.0.0.0:3000/grafana/" +# - name: GF_SERVER_SERVE_FROM_SUB_PATH +# value: "true" +# readinessProbe: +# failureThreshold: 3 +# httpGet: +# path: /robots.txt +# port: 3000 +# scheme: HTTP +# initialDelaySeconds: 10 +# periodSeconds: 30 +# successThreshold: 1 +# timeoutSeconds: 2 +# livenessProbe: +# failureThreshold: 3 +# initialDelaySeconds: 30 +# periodSeconds: 10 +# successThreshold: 1 +# tcpSocket: +# port: 3000 +# timeoutSeconds: 1 +# resources: +# requests: +# cpu: 250m +# memory: 750Mi +# limits: +# cpu: 700m +# memory: 1024Mi --- apiVersion: v1 kind: Service @@ -110,6 +110,6 @@ spec: - name: webui port: 8004 targetPort: 8004 - - name: grafana - port: 3000 - targetPort: 3000 +# - name: grafana +# port: 3000 +# targetPort: 3000 -- GitLab From 29e3f8e805f0e3551dde399e4cb659b1b9fcba61 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 2 Dec 2022 17:14:01 +0000 Subject: [PATCH 074/353] Tool Load Scenario: - added missing headers --- src/tests/tools/load_scenario/run.sh | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/tests/tools/load_scenario/run.sh b/src/tests/tools/load_scenario/run.sh index 5d659c189..0ec0c3725 100755 --- a/src/tests/tools/load_scenario/run.sh +++ b/src/tests/tools/load_scenario/run.sh @@ -1,4 +1,17 @@ #!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. source tfs_runtime_env_vars.sh python -m tests.tools.load_scenario $1 -- GitLab From a637dd445819cb3feac317426466aeec1896d1ac Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 2 Dec 2022 17:16:15 +0000 Subject: [PATCH 075/353] Tools - Load Generator: - created new load generator tool to stress TeraFlowSDN - added descriptor file to populate topologies - added helper script to run it --- src/tests/tools/load_gen/Parameters.py | 48 ++++ src/tests/tools/load_gen/ServiceGenerator.py | 144 ++++++++++++ src/tests/tools/load_gen/ServiceScheduler.py | 106 +++++++++ src/tests/tools/load_gen/__init__.py | 14 ++ src/tests/tools/load_gen/__main__.py | 44 ++++ src/tests/tools/load_gen/deploy_specs.sh | 26 +++ src/tests/tools/load_gen/descriptors.json | 229 +++++++++++++++++++ src/tests/tools/load_gen/run.sh | 17 ++ 8 files changed, 628 insertions(+) create mode 100644 src/tests/tools/load_gen/Parameters.py create mode 100644 src/tests/tools/load_gen/ServiceGenerator.py create mode 100644 src/tests/tools/load_gen/ServiceScheduler.py create mode 100644 src/tests/tools/load_gen/__init__.py create mode 100644 src/tests/tools/load_gen/__main__.py create mode 100644 src/tests/tools/load_gen/deploy_specs.sh create mode 100644 src/tests/tools/load_gen/descriptors.json create mode 100755 src/tests/tools/load_gen/run.sh diff --git a/src/tests/tools/load_gen/Parameters.py b/src/tests/tools/load_gen/Parameters.py new file mode 100644 index 000000000..f3fe742df --- /dev/null +++ b/src/tests/tools/load_gen/Parameters.py @@ -0,0 +1,48 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 typing import Optional + +class Parameters: + def __init__( + self, num_services : int, offered_load : Optional[float] = None, inter_arrival_time : Optional[float] = None, + holding_time : Optional[float] = None, dry_mode : bool = False + ) -> None: + self._num_services = num_services + self._offered_load = offered_load + self._inter_arrival_time = inter_arrival_time + self._holding_time = holding_time + self._dry_mode = dry_mode + + if self._offered_load is None and self._holding_time is not None and self._inter_arrival_time is not None: + self._offered_load = self._holding_time / self._inter_arrival_time + elif self._offered_load is not None and self._holding_time is not None and self._inter_arrival_time is None: + self._inter_arrival_time = self._holding_time / self._offered_load + elif self._offered_load is not None and self._holding_time is None and self._inter_arrival_time is not None: + self._holding_time = self._offered_load * self._inter_arrival_time + + @property + def num_services(self): return self._num_services + + @property + def offered_load(self): return self._offered_load + + @property + def inter_arrival_time(self): return self._inter_arrival_time + + @property + def holding_time(self): return self._holding_time + + @property + def dry_mode(self): return self._dry_mode diff --git a/src/tests/tools/load_gen/ServiceGenerator.py b/src/tests/tools/load_gen/ServiceGenerator.py new file mode 100644 index 000000000..d21c345a3 --- /dev/null +++ b/src/tests/tools/load_gen/ServiceGenerator.py @@ -0,0 +1,144 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, json, random, threading +from typing import Dict, Optional, Set, Tuple +from common.proto.context_pb2 import Empty +from common.tools.object_factory.Constraint import json_constraint_custom +from common.tools.object_factory.ConfigRule import json_config_rule_set +from common.tools.object_factory.Device import json_device_id +from common.tools.object_factory.EndPoint import json_endpoint_id +from common.tools.object_factory.Service import json_service_l2nm_planned +from context.client.ContextClient import ContextClient + +LOGGER = logging.getLogger(__name__) + +class ServiceGenerator: + def __init__(self) -> None: + self._lock = threading.Lock() + self._num_services = 0 + self._available_device_endpoints : Dict[str, Set[str]] = dict() + self._used_device_endpoints : Dict[str, Dict[str, str]] = dict() + + def initialize(self) -> None: + with self._lock: + self._available_device_endpoints.clear() + self._used_device_endpoints.clear() + + context_client = ContextClient() + + devices = context_client.ListDevices(Empty()) + for device in devices.devices: + device_uuid = device.device_id.device_uuid.uuid + _endpoints = self._available_device_endpoints.setdefault(device_uuid, set()) + for endpoint in device.device_endpoints: + endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid + _endpoints.add(endpoint_uuid) + + links = context_client.ListLinks(Empty()) + for link in links.links: + for endpoint_id in link.link_endpoint_ids: + device_uuid = endpoint_id.device_id.device_uuid.uuid + endpoint_uuid = endpoint_id.endpoint_uuid.uuid + _endpoints = self._available_device_endpoints.get(device_uuid, set()) + _endpoints.discard(endpoint_uuid) + if len(_endpoints) == 0: self._available_device_endpoints.pop(device_uuid, None) + + @property + def num_services_generated(self): return self._num_services + + def dump_state(self) -> None: + with self._lock: + _endpoints = { + device_uuid:[endpoint_uuid for endpoint_uuid in endpoint_uuids] + for device_uuid,endpoint_uuids in self._available_device_endpoints.items() + } + LOGGER.info('[dump_state] available_device_endpoints = {:s}'.format(json.dumps(_endpoints))) + LOGGER.info('[dump_state] used_device_endpoints = {:s}'.format(json.dumps(self._used_device_endpoints))) + + def _use_device_endpoint( + self, service_uuid : str, exclude_device_uuids : Set[str] = set() + ) -> Optional[Tuple[str, str]]: + with self._lock: + elegible_device_endpoints = { + device_uuid:device_endpoint_uuids + for device_uuid,device_endpoint_uuids in self._available_device_endpoints.items() + if device_uuid not in exclude_device_uuids and len(device_endpoint_uuids) > 0 + } + if len(elegible_device_endpoints) == 0: return None + device_uuid = random.choice(list(elegible_device_endpoints.keys())) + device_endpoint_uuids = elegible_device_endpoints.get(device_uuid) + endpoint_uuid = random.choice(list(device_endpoint_uuids)) + self._available_device_endpoints.setdefault(device_uuid, set()).discard(endpoint_uuid) + self._used_device_endpoints.setdefault(device_uuid, dict())[endpoint_uuid] = service_uuid + return device_uuid, endpoint_uuid + + def _release_device_endpoint(self, device_uuid : str, endpoint_uuid : str) -> None: + with self._lock: + self._used_device_endpoints.setdefault(device_uuid, set()).pop(endpoint_uuid, None) + self._available_device_endpoints.setdefault(device_uuid, set()).add(endpoint_uuid) + + def compose_service(self) -> Optional[Dict]: + with self._lock: + self._num_services += 1 + num_service = self._num_services + #service_uuid = str(uuid.uuid4()) + service_uuid = 'svc_{:d}'.format(num_service) + src = self._use_device_endpoint(service_uuid) + if src is None: return None + src_device_uuid,src_endpoint_uuid = src + dst = self._use_device_endpoint(service_uuid, exclude_device_uuids={src_device_uuid}) + if dst is None: + self._release_device_endpoint(src_device_uuid, src_endpoint_uuid) + return None + dst_device_uuid,dst_endpoint_uuid = dst + endpoint_ids = [ + json_endpoint_id(json_device_id(src_device_uuid), src_endpoint_uuid), + json_endpoint_id(json_device_id(dst_device_uuid), dst_endpoint_uuid), + ] + constraints = [ + json_constraint_custom('bandwidth[gbps]', '10.0'), + json_constraint_custom('latency[ms]', '20.0'), + ] + vlan_id = num_service % 1000 + circuit_id = '{:03d}'.format(vlan_id) + src_router_id = '.'.join([src_device_uuid.replace('R', ''), '0'] + src_endpoint_uuid.split('/')) + dst_router_id = '.'.join([dst_device_uuid.replace('R', ''), '0'] + dst_endpoint_uuid.split('/')) + config_rules = [ + json_config_rule_set('/settings', { + 'mtu': 1512 + }), + json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(src_device_uuid, src_endpoint_uuid), { + 'router_id': src_router_id, + 'sub_interface_index': vlan_id, + 'vlan_id': vlan_id, + 'remote_router': dst_router_id, + 'circuit_id': circuit_id, + }), + json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(dst_device_uuid, dst_endpoint_uuid), { + 'router_id': dst_router_id, + 'sub_interface_index': vlan_id, + 'vlan_id': vlan_id, + 'remote_router': src_router_id, + 'circuit_id': circuit_id, + }), + ] + return json_service_l2nm_planned( + service_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules) + + def release_service(self, json_service : Dict) -> None: + for endpoint_id in json_service['service_endpoint_ids']: + device_uuid = endpoint_id['device_id']['device_uuid']['uuid'] + endpoint_uuid = endpoint_id['endpoint_uuid']['uuid'] + self._release_device_endpoint(device_uuid, endpoint_uuid) diff --git a/src/tests/tools/load_gen/ServiceScheduler.py b/src/tests/tools/load_gen/ServiceScheduler.py new file mode 100644 index 000000000..5a8b8dbdf --- /dev/null +++ b/src/tests/tools/load_gen/ServiceScheduler.py @@ -0,0 +1,106 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 copy, logging, pytz, random +from datetime import datetime, timedelta +from apscheduler.executors.pool import ThreadPoolExecutor +from apscheduler.jobstores.memory import MemoryJobStore +from apscheduler.schedulers.blocking import BlockingScheduler +from typing import Dict +from common.proto.context_pb2 import Service, ServiceId +from service.client.ServiceClient import ServiceClient +from .Parameters import Parameters +from .ServiceGenerator import ServiceGenerator + +logging.getLogger('apscheduler.executors.default').setLevel(logging.WARNING) +logging.getLogger('apscheduler.scheduler').setLevel(logging.WARNING) + +LOGGER = logging.getLogger(__name__) + +class ServiceScheduler: + def __init__(self, parameters : Parameters, service_generator : ServiceGenerator) -> None: + self._scheduler = BlockingScheduler() + self._scheduler.configure( + jobstores = {'default': MemoryJobStore()}, + executors = {'default': ThreadPoolExecutor(max_workers=10)}, + job_defaults = { + 'coalesce': False, + 'max_instances': 100, + 'misfire_grace_time': 120, + }, + timezone=pytz.utc) + self._parameters = parameters + self._service_generator = service_generator + + def _schedule_service_setup(self) -> None: + if self._service_generator.num_services_generated >= self._parameters.num_services: + LOGGER.info('Generation Done!') + #self._scheduler.shutdown() + return + iat = random.expovariate(1.0 / self._parameters.inter_arrival_time) + run_date = datetime.utcnow() + timedelta(seconds=iat) + self._scheduler.add_job( + self._service_setup, trigger='date', run_date=run_date, timezone=pytz.utc) + + def _schedule_service_teardown(self, service : Dict) -> None: + ht = random.expovariate(1.0 / self._parameters.holding_time) + run_date = datetime.utcnow() + timedelta(seconds=ht) + self._scheduler.add_job( + self._service_teardown, args=(service,), trigger='date', run_date=run_date, timezone=pytz.utc) + + def start(self): + self._schedule_service_setup() + self._scheduler.start() + + def _service_setup(self) -> None: + self._schedule_service_setup() + + service = self._service_generator.compose_service() + if service is None: + LOGGER.warning('No resources available to compose new service') + return + + service_uuid = service['service_id']['service_uuid']['uuid'] + src_device_uuid = service['service_endpoint_ids'][0]['device_id']['device_uuid']['uuid'] + src_endpoint_uuid = service['service_endpoint_ids'][0]['endpoint_uuid']['uuid'] + dst_device_uuid = service['service_endpoint_ids'][1]['device_id']['device_uuid']['uuid'] + dst_endpoint_uuid = service['service_endpoint_ids'][1]['endpoint_uuid']['uuid'] + LOGGER.info('Setup Service: uuid=%s src=%s:%s dst=%s:%s', + service_uuid, src_device_uuid, src_endpoint_uuid, dst_device_uuid, dst_endpoint_uuid) + + if not self._parameters.dry_mode: + service_add = copy.deepcopy(service) + service_add['service_endpoint_ids'] = [] + service_add['service_constraints'] = [] + service_add['service_config'] = {'config_rules': []} + service_client = ServiceClient() # create instances per request to load balance between pods + service_client.CreateService(Service(**service_add)) + service_client.UpdateService(Service(**service)) + + self._schedule_service_teardown(service) + + def _service_teardown(self, service : Dict) -> None: + service_uuid = service['service_id']['service_uuid']['uuid'] + src_device_uuid = service['service_endpoint_ids'][0]['device_id']['device_uuid']['uuid'] + src_endpoint_uuid = service['service_endpoint_ids'][0]['endpoint_uuid']['uuid'] + dst_device_uuid = service['service_endpoint_ids'][1]['device_id']['device_uuid']['uuid'] + dst_endpoint_uuid = service['service_endpoint_ids'][1]['endpoint_uuid']['uuid'] + LOGGER.info('Teardown Service: uuid=%s src=%s:%s dst=%s:%s', + service_uuid, src_device_uuid, src_endpoint_uuid, dst_device_uuid, dst_endpoint_uuid) + + if not self._parameters.dry_mode: + service_client = ServiceClient() # create instances per request to load balance between pods + service_client.DeleteService(ServiceId(**(service['service_id']))) + + self._service_generator.release_service(service) diff --git a/src/tests/tools/load_gen/__init__.py b/src/tests/tools/load_gen/__init__.py new file mode 100644 index 000000000..70a332512 --- /dev/null +++ b/src/tests/tools/load_gen/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/tests/tools/load_gen/__main__.py b/src/tests/tools/load_gen/__main__.py new file mode 100644 index 000000000..7a81dbcba --- /dev/null +++ b/src/tests/tools/load_gen/__main__.py @@ -0,0 +1,44 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, sys +from .Parameters import Parameters +from .ServiceGenerator import ServiceGenerator +from .ServiceScheduler import ServiceScheduler + +logging.basicConfig(level=logging.INFO) +LOGGER = logging.getLogger(__name__) + +def main(): + LOGGER.info('Starting...') + parameters = Parameters( + num_services = 100, + offered_load = 50, + holding_time = 10, + dry_mode = False, # in dry mode, no request is sent to TeraFlowSDN + ) + + LOGGER.info('Initializing Generator...') + service_generator = ServiceGenerator() + service_generator.initialize() + + LOGGER.info('Running Schedule...') + scheduler = ServiceScheduler(parameters, service_generator) + scheduler.start() + + LOGGER.info('Done!') + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/tests/tools/load_gen/deploy_specs.sh b/src/tests/tools/load_gen/deploy_specs.sh new file mode 100644 index 000000000..238918480 --- /dev/null +++ b/src/tests/tools/load_gen/deploy_specs.sh @@ -0,0 +1,26 @@ +# Set the URL of your local Docker registry where the images will be uploaded to. +export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" + +# Set the list of components, separated by spaces, you want to build images for, and deploy. +# Supported components are: +# context device automation policy service compute monitoring webui +# interdomain slice pathcomp dlt +# dbscanserving opticalattackmitigator opticalattackdetector +# l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector +export TFS_COMPONENTS="context device pathcomp service slice webui" # automation monitoring compute + +# Set the tag you want to use for your images. +export TFS_IMAGE_TAG="dev" + +# Set the name of the Kubernetes namespace to deploy to. +export TFS_K8S_NAMESPACE="tfs" + +# Set additional manifest files to be applied after the deployment +export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml manifests/servicemonitors.yaml" + +# Set the new Grafana admin password +export TFS_GRAFANA_PASSWORD="admin123+" + +# If not already set, disable skip-build flag. +# If TFS_SKIP_BUILD is "YES", the containers are not rebuilt-retagged-repushed and existing ones are used. +export TFS_SKIP_BUILD="NO" #${TFS_SKIP_BUILD:-"YES"} diff --git a/src/tests/tools/load_gen/descriptors.json b/src/tests/tools/load_gen/descriptors.json new file mode 100644 index 000000000..5fb0c0867 --- /dev/null +++ b/src/tests/tools/load_gen/descriptors.json @@ -0,0 +1,229 @@ +{ + "contexts": [ + { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_ids": [], "service_ids": [] + } + ], + "topologies": [ + { + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "admin"} + }, + "device_ids": [ + {"device_uuid": {"uuid": "R1"}}, + {"device_uuid": {"uuid": "R2"}}, + {"device_uuid": {"uuid": "R3"}}, + {"device_uuid": {"uuid": "R4"}}, + {"device_uuid": {"uuid": "R5"}}, + {"device_uuid": {"uuid": "R6"}}, + {"device_uuid": {"uuid": "R7"}} + ], + "link_ids": [ + {"link_uuid": {"uuid": "R1==R2"}}, + {"link_uuid": {"uuid": "R2==R3"}}, + {"link_uuid": {"uuid": "R3==R4"}}, + {"link_uuid": {"uuid": "R4==R5"}}, + {"link_uuid": {"uuid": "R5==R6"}}, + {"link_uuid": {"uuid": "R6==R1"}}, + {"link_uuid": {"uuid": "R1==R7"}}, + {"link_uuid": {"uuid": "R3==R7"}}, + {"link_uuid": {"uuid": "R5==R7"}} + ] + } + ], + "devices": [ + { + "device_id": {"device_uuid": {"uuid": "R1"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R2"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R3"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R4"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R5"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R6"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R7"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"}, + {"sample_types": [], "type": "copper", "uuid": "2/4"}, + {"sample_types": [], "type": "copper", "uuid": "2/5"}, + {"sample_types": [], "type": "copper", "uuid": "2/6"} + ]}}} + ]} + } + ], + "links": [ + { + "link_id": {"link_uuid": {"uuid": "R1==R2"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R2==R3"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R3==R4"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R4==R5"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R5==R6"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R6"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R6==R1"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R6"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R1==R7"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/3"}}, + {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R3==R7"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/3"}}, + {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/3"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R5==R7"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/3"}}, + {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/5"}} + ] + } + ] +} \ No newline at end of file diff --git a/src/tests/tools/load_gen/run.sh b/src/tests/tools/load_gen/run.sh new file mode 100755 index 000000000..b16808ab6 --- /dev/null +++ b/src/tests/tools/load_gen/run.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +source tfs_runtime_env_vars.sh +python -m tests.tools.load_gen -- GitLab From c47c372f107c6032da2ee1f01776481393704370 Mon Sep 17 00:00:00 2001 From: cmanso Date: Sun, 11 Dec 2022 15:47:46 +0100 Subject: [PATCH 076/353] Update scalability --- src/context/service/Database.py | 50 +++-- src/context/service/database/ConfigModel.py | 10 +- src/context/service/database/DeviceModel.py | 25 +-- src/context/service/database/EndPointModel.py | 2 +- src/context/service/database/LinkModel.py | 54 +++--- .../service/database/RelationModels.py | 103 ++++++----- src/context/service/database/ServiceModel.py | 28 ++- src/context/service/database/TopologyModel.py | 21 +-- .../grpc_server/ContextServiceServicerImpl.py | 171 ++++++++++++------ src/context/tests/Objects.py | 35 ++-- 10 files changed, 271 insertions(+), 228 deletions(-) diff --git a/src/context/service/Database.py b/src/context/service/Database.py index bf970b356..2b699203a 100644 --- a/src/context/service/Database.py +++ b/src/context/service/Database.py @@ -16,6 +16,9 @@ class Database(Session): super().__init__() self.session = session + def get_session(self): + return self.session + def get_all(self, model): result = [] with self.session() as session: @@ -27,22 +30,21 @@ class Database(Session): def create_or_update(self, model): with self.session() as session: att = getattr(model, model.main_pk_name()) - obj = self.get_object(type(model), att) - filt = {model.main_pk_name(): att} t_model = type(model) - found = session.query(t_model).filter_by(**filt).one_or_none() - if found: + obj = session.query(t_model).filter_by(**filt).one_or_none() + + if obj: + for key in obj.__table__.columns.keys(): + setattr(obj, key, getattr(model, key)) found = True + session.commit() + return obj, found else: found = False - - session.merge(model) - session.commit() - - obj = self.get_object(t_model, att) - - return model, found + session.add(model) + session.commit() + return model, found def create(self, model): with self.session() as session: @@ -85,7 +87,6 @@ class Database(Session): for table in meta.sorted_tables: for row in engine.execute(table.select()): result.append((table.name, dict(row))) - LOGGER.info(result) return result @@ -98,10 +99,27 @@ class Database(Session): if raise_if_not_found: raise NotFoundException(model_class.__name__.replace('Model', ''), main_key) - return get + dump = None + if hasattr(get, 'dump'): + dump = get.dump() + return get, dump + + def get_object_filter(self, model_class: Base, filt, raise_if_not_found=False): + with self.session() as session: + get = session.query(model_class).filter_by(**filt).all() + + if not get: + if raise_if_not_found: + raise NotFoundException(model_class.__name__.replace('Model', '')) + else: + return None, None + + if isinstance(get, list): + return get, [obj.dump() for obj in get] + + return get, get.dump() - def get_or_create(self, model_class: Base, key_parts: List[str], filt=None) -> Tuple[Base, bool]: - str_key = key_to_str(key_parts) + def get_or_create(self, model_class: Base, key_parts: str, filt=None) -> Tuple[Base, bool]: if not filt: filt = {model_class.main_pk_name(): key_parts} with self.session() as session: @@ -110,7 +128,7 @@ class Database(Session): return get, False else: obj = model_class() - setattr(obj, model_class.main_pk_name(), str_key) + setattr(obj, model_class.main_pk_name(), key_parts) session.add(obj) session.commit() return obj, True diff --git a/src/context/service/database/ConfigModel.py b/src/context/service/database/ConfigModel.py index 40069185f..2ec22985c 100644 --- a/src/context/service/database/ConfigModel.py +++ b/src/context/service/database/ConfigModel.py @@ -40,13 +40,7 @@ class ConfigModel(Base): # pylint: disable=abstract-method config_uuid = Column(UUID(as_uuid=False), primary_key=True) # Relationships - config_rule = relationship("ConfigRuleModel", back_populates="config", lazy='joined') - - - def delete(self) -> None: - db_config_rule_pks = self.references(ConfigRuleModel) - for pk,_ in db_config_rule_pks: ConfigRuleModel(self.database, pk).delete() - super().delete() + config_rule = relationship("ConfigRuleModel", cascade="all,delete", back_populates="config", lazy='joined') def dump(self) -> List[Dict]: config_rules = [] @@ -75,7 +69,7 @@ class ConfigRuleModel(Base): # pylint: disable=abstract-method ) # Relationships - config = relationship("ConfigModel", back_populates="config_rule") + config = relationship("ConfigModel", passive_deletes=True, back_populates="config_rule") def dump(self, include_position=True) -> Dict: # pylint: disable=arguments-differ result = { diff --git a/src/context/service/database/DeviceModel.py b/src/context/service/database/DeviceModel.py index 122da50af..b7e7efed4 100644 --- a/src/context/service/database/DeviceModel.py +++ b/src/context/service/database/DeviceModel.py @@ -54,30 +54,10 @@ class DeviceModel(Base): native_enum=False)) # Relationships - device_config = relationship("ConfigModel", passive_deletes="all, delete", lazy="joined") + device_config = relationship("ConfigModel", passive_deletes=True, lazy="joined") driver = relationship("DriverModel", passive_deletes=True, back_populates="device") endpoints = relationship("EndPointModel", passive_deletes=True, back_populates="device") - # topology = relationship("TopologyModel", lazy="joined") - - # def delete(self) -> None: - # # pylint: disable=import-outside-toplevel - # from .EndPointModel import EndPointModel - # from .RelationModels import TopologyDeviceModel - # - # for db_endpoint_pk,_ in self.references(EndPointModel): - # EndPointModel(self.database, db_endpoint_pk).delete() - # - # for db_topology_device_pk,_ in self.references(TopologyDeviceModel): - # TopologyDeviceModel(self.database, db_topology_device_pk).delete() - # - # for db_driver_pk,_ in self.references(DriverModel): - # DriverModel(self.database, db_driver_pk).delete() - # - # super().delete() - # - # ConfigModel(self.database, self.device_config_fk).delete() - def dump_id(self) -> Dict: return {'device_uuid': {'uuid': self.device_uuid}} @@ -86,10 +66,7 @@ class DeviceModel(Base): def dump_drivers(self) -> List[int]: response = [] - for a in self.driver: - LOGGER.info('DUMPPPPPPPPPPPPPPPPPPPPPIIIIIIIIIIIIIIIIIIIIIIINNNNNNNNNNNNNNNGGGGGGGGGGGGGGGGGGg') - LOGGER.info('aasdfadsf: {}'.format(a.dump())) response.append(a.dump()) return response diff --git a/src/context/service/database/EndPointModel.py b/src/context/service/database/EndPointModel.py index a4381a2e3..fb2c9d26a 100644 --- a/src/context/service/database/EndPointModel.py +++ b/src/context/service/database/EndPointModel.py @@ -20,7 +20,7 @@ from common.orm.backend.Tools import key_to_str from common.proto.context_pb2 import EndPointId from .KpiSampleType import ORM_KpiSampleTypeEnum, grpc_to_enum__kpi_sample_type from sqlalchemy import Column, ForeignKey, String, Enum, ForeignKeyConstraint -from sqlalchemy.dialects.postgresql import UUID, ARRAY +from sqlalchemy.dialects.postgresql import UUID from context.service.database.Base import Base from sqlalchemy.orm import relationship LOGGER = logging.getLogger(__name__) diff --git a/src/context/service/database/LinkModel.py b/src/context/service/database/LinkModel.py index 8f1d971c3..025709dfd 100644 --- a/src/context/service/database/LinkModel.py +++ b/src/context/service/database/LinkModel.py @@ -14,39 +14,39 @@ import logging, operator from typing import Dict, List -from common.orm.fields.PrimaryKeyField import PrimaryKeyField -from common.orm.fields.StringField import StringField -from common.orm.model.Model import Model -from common.orm.HighLevel import get_related_objects +from sqlalchemy import Column, ForeignKey +from sqlalchemy.dialects.postgresql import UUID +from context.service.database.Base import Base +from sqlalchemy.orm import relationship LOGGER = logging.getLogger(__name__) -class LinkModel(Model): - pk = PrimaryKeyField() - link_uuid = StringField(required=True, allow_empty=False) +class LinkModel(Base): + __tablename__ = 'Link' + link_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) - def delete(self) -> None: - #pylint: disable=import-outside-toplevel - from .RelationModels import LinkEndPointModel, TopologyLinkModel - - for db_link_endpoint_pk,_ in self.references(LinkEndPointModel): - LinkEndPointModel(self.database, db_link_endpoint_pk).delete() - - for db_topology_link_pk,_ in self.references(TopologyLinkModel): - TopologyLinkModel(self.database, db_topology_link_pk).delete() - - super().delete() + @staticmethod + def main_pk_name(): + return 'link_uuid' def dump_id(self) -> Dict: return {'link_uuid': {'uuid': self.link_uuid}} def dump_endpoint_ids(self) -> List[Dict]: - from .RelationModels import LinkEndPointModel # pylint: disable=import-outside-toplevel - db_endpoints = get_related_objects(self, LinkEndPointModel, 'endpoint_fk') - return [db_endpoint.dump_id() for db_endpoint in sorted(db_endpoints, key=operator.attrgetter('pk'))] - - def dump(self) -> Dict: - return { - 'link_id': self.dump_id(), - 'link_endpoint_ids': self.dump_endpoint_ids(), - } + return [endpoint.dump_id() for endpoint in self.endpoints] + + def dump(self, endpoints=None) -> Dict: + result = { + 'link_id': self.dump_id() + } + if endpoints: + result['link_endpoint_ids'] = [] + for endpoint in endpoints: + dump = endpoint.dump_id() + LOGGER.info(dump) + result['link_endpoint_ids'].append(dump) + + LOGGER.info(result['link_endpoint_ids']) + + LOGGER.info(result) + return result diff --git a/src/context/service/database/RelationModels.py b/src/context/service/database/RelationModels.py index 98b077a77..e69feadc4 100644 --- a/src/context/service/database/RelationModels.py +++ b/src/context/service/database/RelationModels.py @@ -13,55 +13,68 @@ # limitations under the License. import logging -from common.orm.fields.ForeignKeyField import ForeignKeyField -from common.orm.fields.PrimaryKeyField import PrimaryKeyField -from common.orm.model.Model import Model -from .ConnectionModel import ConnectionModel -from .DeviceModel import DeviceModel -from .EndPointModel import EndPointModel -from .LinkModel import LinkModel -from .ServiceModel import ServiceModel -from .SliceModel import SliceModel -from .TopologyModel import TopologyModel +from sqlalchemy import Column, ForeignKey +from sqlalchemy.dialects.postgresql import UUID +from context.service.database.Base import Base LOGGER = logging.getLogger(__name__) +# +# class ConnectionSubServiceModel(Model): # pylint: disable=abstract-method +# pk = PrimaryKeyField() +# connection_fk = ForeignKeyField(ConnectionModel) +# sub_service_fk = ForeignKeyField(ServiceModel) +# +class LinkEndPointModel(Base): # pylint: disable=abstract-method + __tablename__ = 'LinkEndPoint' + # uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) + link_uuid = Column(UUID(as_uuid=False), ForeignKey("Link.link_uuid")) + endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid"), primary_key=True) -class ConnectionSubServiceModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - connection_fk = ForeignKeyField(ConnectionModel) - sub_service_fk = ForeignKeyField(ServiceModel) - -class LinkEndPointModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - link_fk = ForeignKeyField(LinkModel) - endpoint_fk = ForeignKeyField(EndPointModel) - -class ServiceEndPointModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - service_fk = ForeignKeyField(ServiceModel) - endpoint_fk = ForeignKeyField(EndPointModel) - -class SliceEndPointModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - slice_fk = ForeignKeyField(SliceModel) - endpoint_fk = ForeignKeyField(EndPointModel) + @staticmethod + def main_pk_name(): + return 'endpoint_uuid' -class SliceServiceModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - slice_fk = ForeignKeyField(SliceModel) - service_fk = ForeignKeyField(ServiceModel) +# +# class ServiceEndPointModel(Model): # pylint: disable=abstract-method +# pk = PrimaryKeyField() +# service_fk = ForeignKeyField(ServiceModel) +# endpoint_fk = ForeignKeyField(EndPointModel) +# +# class SliceEndPointModel(Model): # pylint: disable=abstract-method +# pk = PrimaryKeyField() +# slice_fk = ForeignKeyField(SliceModel) +# endpoint_fk = ForeignKeyField(EndPointModel) +# +# class SliceServiceModel(Model): # pylint: disable=abstract-method +# pk = PrimaryKeyField() +# slice_fk = ForeignKeyField(SliceModel) +# service_fk = ForeignKeyField(ServiceMo# pylint: disable=abstract-method +# __tablename__ = 'LinkEndPoint' +# uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) +# link_uuid = Column(UUID(as_uuid=False), ForeignKey("Link.link_uuid")) +# endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid")) +#del) +# +# class SliceSubSliceModel(Model): # pylint: disable=abstract-method +# pk = PrimaryKeyField() +# slice_fk = ForeignKeyField(SliceModel) +# sub_slice_fk = ForeignKeyField(SliceModel) -class SliceSubSliceModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - slice_fk = ForeignKeyField(SliceModel) - sub_slice_fk = ForeignKeyField(SliceModel) +class TopologyDeviceModel(Base): # pylint: disable=abstract-method + __tablename__ = 'TopologyDevice' + # uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) + topology_uuid = Column(UUID(as_uuid=False), ForeignKey("Topology.topology_uuid")) + device_uuid = Column(UUID(as_uuid=False), ForeignKey("Device.device_uuid"), primary_key=True) -class TopologyDeviceModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - topology_fk = ForeignKeyField(TopologyModel) - device_fk = ForeignKeyField(DeviceModel) + @staticmethod + def main_pk_name(): + return 'device_uuid' +# +class TopologyLinkModel(Base): # pylint: disable=abstract-method + __tablename__ = 'TopologyLink' + topology_uuid = Column(UUID(as_uuid=False), ForeignKey("Topology.topology_uuid")) + link_uuid = Column(UUID(as_uuid=False), ForeignKey("Link.link_uuid"), primary_key=True) -class TopologyLinkModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - topology_fk = ForeignKeyField(TopologyModel) - link_fk = ForeignKeyField(LinkModel) + @staticmethod + def main_pk_name(): + return 'link_uuid' \ No newline at end of file diff --git a/src/context/service/database/ServiceModel.py b/src/context/service/database/ServiceModel.py index 8b32d1cc9..a5223d615 100644 --- a/src/context/service/database/ServiceModel.py +++ b/src/context/service/database/ServiceModel.py @@ -13,20 +13,17 @@ # limitations under the License. import functools, logging, operator -from enum import Enum +from sqlalchemy import Column, ForeignKey, String, Enum from typing import Dict, List -from common.orm.fields.EnumeratedField import EnumeratedField -from common.orm.fields.ForeignKeyField import ForeignKeyField -from common.orm.fields.PrimaryKeyField import PrimaryKeyField -from common.orm.fields.StringField import StringField -from common.orm.model.Model import Model from common.orm.HighLevel import get_related_objects from common.proto.context_pb2 import ServiceStatusEnum, ServiceTypeEnum from .ConfigModel import ConfigModel from .ConstraintModel import ConstraintsModel from .ContextModel import ContextModel from .Tools import grpc_to_enum - +from sqlalchemy import Column, ForeignKey +from sqlalchemy.dialects.postgresql import UUID +from context.service.database.Base import Base LOGGER = logging.getLogger(__name__) class ORM_ServiceTypeEnum(Enum): @@ -47,14 +44,15 @@ class ORM_ServiceStatusEnum(Enum): grpc_to_enum__service_status = functools.partial( grpc_to_enum, ServiceStatusEnum, ORM_ServiceStatusEnum) -class ServiceModel(Model): - pk = PrimaryKeyField() - context_fk = ForeignKeyField(ContextModel) - service_uuid = StringField(required=True, allow_empty=False) - service_type = EnumeratedField(ORM_ServiceTypeEnum, required=True) - service_constraints_fk = ForeignKeyField(ConstraintsModel) - service_status = EnumeratedField(ORM_ServiceStatusEnum, required=True) - service_config_fk = ForeignKeyField(ConfigModel) +class ServiceModel(Base): + __tablename__ = 'Service' + + service_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) + service_type = Column(Enum(ORM_ServiceTypeEnum, create_constraint=False, native_enum=False, allow_empty=False)) + # service_constraints = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid", ondelete='SET NULL')) + # context_fk = ForeignKeyField(ContextModel) + service_status = Column(Enum(ORM_ServiceStatusEnum, create_constraint=False, native_enum=False, allow_empty=False)) + # service_config_fk = ForeignKeyField(ConfigModel) def delete(self) -> None: #pylint: disable=import-outside-toplevel diff --git a/src/context/service/database/TopologyModel.py b/src/context/service/database/TopologyModel.py index 2925a27fa..063a1f511 100644 --- a/src/context/service/database/TopologyModel.py +++ b/src/context/service/database/TopologyModel.py @@ -26,7 +26,7 @@ class TopologyModel(Base): topology_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) # Relationships - context = relationship("ContextModel", back_populates="topology", lazy="joined") + context = relationship("ContextModel", back_populates="topology") def dump_id(self) -> Dict: context_id = self.context.dump_id() @@ -39,21 +39,12 @@ class TopologyModel(Base): def main_pk_name() -> str: return 'topology_uuid' - """def dump_device_ids(self) -> List[Dict]: - from .RelationModels import TopologyDeviceModel # pylint: disable=import-outside-toplevel - db_devices = get_related_objects(self, TopologyDeviceModel, 'device_fk') - return [db_device.dump_id() for db_device in sorted(db_devices, key=operator.attrgetter('pk'))] - - def dump_link_ids(self) -> List[Dict]: - from .RelationModels import TopologyLinkModel # pylint: disable=import-outside-toplevel - db_links = get_related_objects(self, TopologyLinkModel, 'link_fk') - return [db_link.dump_id() for db_link in sorted(db_links, key=operator.attrgetter('pk'))] - """ - def dump( # pylint: disable=arguments-differ - self, include_devices=True, include_links=True + self, devices=None, links=None ) -> Dict: result = {'topology_id': self.dump_id()} - # if include_devices: result['device_ids'] = self.dump_device_ids() - # if include_links: result['link_ids'] = self.dump_link_ids() + if devices: + result['device_ids'] = [device.dump_id() for device in devices] + if links: + result['link_ids'] = [link.dump_id() for link in links] return result diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py index 108ab9950..264ae3198 100644 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py @@ -60,6 +60,7 @@ from context.service.database.Events import notify_event from context.service.database.EndPointModel import EndPointModel from context.service.database.EndPointModel import KpiSampleTypeModel from context.service.database.LinkModel import LinkModel +from context.service.database.RelationModels import (TopologyDeviceModel, TopologyLinkModel, LinkEndPointModel) from .Constants import ( CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE, @@ -202,16 +203,30 @@ class ContextServiceServicerImpl(ContextServiceServicer): @safe_and_metered_rpc_method(METRICS, LOGGER) def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology: - context_uuid = request.context_id.context_uuid.uuid topology_uuid = request.topology_uuid.uuid + result, dump = self.database.get_object(TopologyModel, topology_uuid, True) with self.session() as session: - result = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).options(contains_eager(TopologyModel.context)).one_or_none() + devs = None + links = None - if not result: - raise NotFoundException(TopologyModel.__name__.replace('Model', ''), topology_uuid) + filt = {'topology_uuid': topology_uuid} + topology_devices = session.query(TopologyDeviceModel).filter_by(**filt).all() + if topology_devices: + devs = [] + for td in topology_devices: + filt = {'device_uuid': td.device_uuid} + devs.append(session.query(DeviceModel).filter_by(**filt).one()) + + filt = {'topology_uuid': topology_uuid} + topology_links = session.query(TopologyLinkModel).filter_by(**filt).all() + if topology_links: + links = [] + for tl in topology_links: + filt = {'link_uuid': tl.link_uuid} + links.append(session.query(LinkModel).filter_by(**filt).one()) - return Topology(**result.dump()) + return Topology(**result.dump(devs, links)) @safe_and_metered_rpc_method(METRICS, LOGGER) @@ -221,15 +236,30 @@ class ContextServiceServicerImpl(ContextServiceServicer): with self.session() as session: topology_add = TopologyModel(topology_uuid=topology_uuid, context_uuid=context_uuid) updated = True - result = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).one_or_none() - if not result: + db_topology = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).one_or_none() + if not db_topology: updated = False session.merge(topology_add) session.commit() - result = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).one_or_none() + db_topology = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).one_or_none() + + for device_id in request.device_ids: + device_uuid = device_id.device_uuid.uuid + td = TopologyDeviceModel(topology_uuid=topology_uuid, device_uuid=device_uuid) + result: Tuple[TopologyDeviceModel, bool] = self.database.create_or_update(td) + + + for link_id in request.link_ids: + link_uuid = link_id.link_uuid.uuid + db_link = session.query(LinkModel).filter( + LinkModel.link_uuid == link_uuid).one_or_none() + tl = TopologyLinkModel(topology_uuid=topology_uuid, link_uuid=link_uuid) + result: Tuple[TopologyDeviceModel, bool] = self.database.create_or_update(tl) + + event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_topology_id = result.dump_id() + dict_topology_id = db_topology.dump_id() notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) return TopologyId(**dict_topology_id) @@ -289,9 +319,10 @@ class ContextServiceServicerImpl(ContextServiceServicer): with self.session() as session: device_uuid = request.device_id.device_uuid.uuid - for i,endpoint in enumerate(request.device_endpoints): + for i, endpoint in enumerate(request.device_endpoints): endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid - if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid + if len(endpoint_device_uuid) == 0: + endpoint_device_uuid = device_uuid if device_uuid != endpoint_device_uuid: raise InvalidArgumentException( 'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid, @@ -313,12 +344,12 @@ class ContextServiceServicerImpl(ContextServiceServicer): self.set_drivers(db_device, request.device_drivers) - for i,endpoint in enumerate(request.device_endpoints): + for i, endpoint in enumerate(request.device_endpoints): endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid - endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid - if len(endpoint_device_uuid) == 0: endpoint_device_uuid = device_uuid + # endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid + # if len(endpoint_device_uuid) == 0: + # endpoint_device_uuid = device_uuid - str_endpoint_key = key_to_str([device_uuid, endpoint_uuid]) endpoint_attributes = { 'device_uuid' : db_device.device_uuid, 'endpoint_uuid': endpoint_uuid, @@ -328,17 +359,19 @@ class ContextServiceServicerImpl(ContextServiceServicer): endpoint_topology_context_uuid = endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid endpoint_topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: - str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) + # str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) - db_topology: TopologyModel = self.database.get_object(TopologyModel, endpoint_topology_uuid) - new_topo = TopologyModel(context_uuid=db_topology.context_uuid, topology_uuid=db_topology.topology_uuid, device_uuids=db_device.device_uuid) + db_topology, topo_dump = self.database.get_object(TopologyModel, endpoint_topology_uuid) - self.database.create_or_update(new_topo) + topology_device = TopologyDeviceModel( + topology_uuid=endpoint_topology_uuid, + device_uuid=db_device.device_uuid) + self.database.create_or_update(topology_device) endpoint_attributes['topology_uuid'] = db_topology.topology_uuid new_endpoint = EndPointModel(**endpoint_attributes) - result : Tuple[EndPointModel, bool] = self.database.create_or_update(new_endpoint) + result: Tuple[EndPointModel, bool] = self.database.create_or_update(new_endpoint) db_endpoint, updated = result self.set_kpi_sample_types(db_endpoint, endpoint.kpi_sample_types) @@ -465,10 +498,15 @@ class ContextServiceServicerImpl(ContextServiceServicer): device_uuid = request.device_uuid.uuid with self.session() as session: - result = session.query(DeviceModel).filter_by(device_uuid=device_uuid).one_or_none() - if not result: + db_device = session.query(DeviceModel).filter_by(device_uuid=device_uuid).one_or_none() + + session.query(TopologyDeviceModel).filter_by(device_uuid=device_uuid).delete() + session.query(ConfigRuleModel).filter_by(config_uuid=db_device.device_config_uuid).delete() + session.query(ConfigModel).filter_by(config_uuid=db_device.device_config_uuid).delete() + + if not db_device: return Empty() - dict_device_id = result.dump_id() + dict_device_id = db_device.dump_id() session.query(DeviceModel).filter_by(device_uuid=device_uuid).delete() session.commit() @@ -496,19 +534,41 @@ class ContextServiceServicerImpl(ContextServiceServicer): @safe_and_metered_rpc_method(METRICS, LOGGER) def ListLinks(self, request: Empty, context : grpc.ServicerContext) -> LinkList: with self.session() as session: - result = session.query(DeviceModel).all() - return LinkList(links=[db_link.dump() for db_link in result]) + link_list = LinkList() + + db_links = session.query(LinkModel).all() + + for db_link in db_links: + link_uuid = db_link.link_uuid + filt = {'link_uuid': link_uuid} + link_endpoints = session.query(LinkEndPointModel).filter_by(**filt).all() + if link_endpoints: + eps = [] + for lep in link_endpoints: + filt = {'endpoint_uuid': lep.endpoint_uuid} + eps.append(session.query(EndPointModel).filter_by(**filt).one()) + link_list.links.append(Link(**db_link.dump(eps))) + + return link_list @safe_and_metered_rpc_method(METRICS, LOGGER) def GetLink(self, request: LinkId, context : grpc.ServicerContext) -> Link: link_uuid = request.link_uuid.uuid with self.session() as session: - result = session.query(LinkModel).filter(LinkModel.device_uuid == link_uuid).one_or_none() + result = session.query(LinkModel).filter(LinkModel.link_uuid == link_uuid).one_or_none() if not result: - raise NotFoundException(DeviceModel.__name__.replace('Model', ''), link_uuid) + raise NotFoundException(LinkModel.__name__.replace('Model', ''), link_uuid) - rd = result.dump() + filt = {'link_uuid': link_uuid} + link_endpoints = session.query(LinkEndPointModel).filter_by(**filt).all() + if link_endpoints: + eps = [] + for lep in link_endpoints: + filt = {'endpoint_uuid': lep.endpoint_uuid} + eps.append(session.query(EndPointModel).filter_by(**filt).one()) + return Link(**result.dump(eps)) + rd = result.dump() rt = Link(**rd) return rt @@ -520,7 +580,7 @@ class ContextServiceServicerImpl(ContextServiceServicer): link_uuid = request.link_id.link_uuid.uuid new_link = LinkModel(**{ - 'lin_uuid': link_uuid + 'link_uuid': link_uuid }) result: Tuple[LinkModel, bool] = self.database.create_or_update(new_link) db_link, updated = result @@ -531,33 +591,20 @@ class ContextServiceServicerImpl(ContextServiceServicer): endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid - str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) + db_topology = None if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: - str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) - # db_topology : TopologyModel = get_object(self.database, TopologyModel, str_topology_key) - db_topology : TopologyModel = self.database.get_object(TopologyModel, str_topology_key) - str_topology_device_key = key_to_str([str_topology_key, endpoint_device_uuid], separator='--') + db_topology: TopologyModel = self.database.get_object(TopologyModel, endpoint_topology_uuid) # check device is in topology - # get_object(self.database, TopologyDeviceModel, str_topology_device_key) - # str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') - - # db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) - LOGGER.info('str_endpoint_key: {}'.format(str_endpoint_key)) - db_endpoint: EndPointModel = self.database.get_object(EndPointModel, str_endpoint_key) - - # str_link_endpoint_key = key_to_str([link_uuid, endpoint_device_uuid], separator='--') - # result : Tuple[LinkEndPointModel, bool] = get_or_create_object( - # self.database, LinkEndPointModel, str_link_endpoint_key, { - # 'link_fk': db_link, 'endpoint_fk': db_endpoint}) - #db_link_endpoint, link_endpoint_created = result - - # if db_topology is not None: - # str_topology_link_key = key_to_str([str_topology_key, link_uuid], separator='--') - # result : Tuple[TopologyLinkModel, bool] = get_or_create_object( - # self.database, TopologyLinkModel, str_topology_link_key, { - # 'topology_fk': db_topology, 'link_fk': db_link}) - # #db_topology_link, topology_link_created = result + self.database.get_object(TopologyDeviceModel, endpoint_device_uuid) + + + link_endpoint = LinkEndPointModel(link_uuid=link_uuid, endpoint_uuid=endpoint_uuid) + result: Tuple[LinkEndPointModel, bool] = self.database.create_or_update(link_endpoint) + + if db_topology is not None: + topology_link = TopologyLinkModel(topology_uuid=endpoint_topology_uuid, link_uuid=link_uuid) + result: Tuple[TopologyLinkModel, bool] = self.database.create_or_update(topology_link) event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE dict_link_id = db_link.dump_id() @@ -566,15 +613,19 @@ class ContextServiceServicerImpl(ContextServiceServicer): @safe_and_metered_rpc_method(METRICS, LOGGER) def RemoveLink(self, request: LinkId, context : grpc.ServicerContext) -> Empty: - with self.lock: + with self.session() as session: link_uuid = request.link_uuid.uuid - db_link = LinkModel(self.database, link_uuid, auto_load=False) - found = db_link.load() - if not found: return Empty() - dict_link_id = db_link.dump_id() - db_link.delete() + session.query(TopologyLinkModel).filter_by(link_uuid=link_uuid).delete() + session.query(LinkEndPointModel).filter_by(link_uuid=link_uuid).delete() + + result = session.query(LinkModel).filter_by(link_uuid=link_uuid).one_or_none() + if not result: + return Empty() + dict_link_id = result.dump_id() + session.query(LinkModel).filter_by(link_uuid=link_uuid).delete() + session.commit() event_type = EventTypeEnum.EVENTTYPE_REMOVE notify_event(self.messagebroker, TOPIC_LINK, event_type, {'link_id': dict_link_id}) return Empty() @@ -584,7 +635,6 @@ class ContextServiceServicerImpl(ContextServiceServicer): for message in self.messagebroker.consume({TOPIC_LINK}, consume_timeout=CONSUME_TIMEOUT): yield LinkEvent(**json.loads(message.content)) - """ # ----- Service ---------------------------------------------------------------------------------------------------- @@ -693,6 +743,7 @@ class ContextServiceServicerImpl(ContextServiceServicer): for message in self.messagebroker.consume({TOPIC_SERVICE}, consume_timeout=CONSUME_TIMEOUT): yield ServiceEvent(**json.loads(message.content)) + """ # ----- Slice ---------------------------------------------------------------------------------------------------- diff --git a/src/context/tests/Objects.py b/src/context/tests/Objects.py index 772da38e0..a2aebdd96 100644 --- a/src/context/tests/Objects.py +++ b/src/context/tests/Objects.py @@ -45,6 +45,7 @@ PACKET_PORT_SAMPLE_TYPES = [ # ----- Device --------------------------------------------------------------------------------------------------------- +EP1 = '5610e2c0-8abe-4127-80d0-7c68aff1c19e' EP2 = '7eb80584-2587-4e71-b10c-f3a5c48e84ab' EP3 = '368baf47-0540-4ab4-add8-a19b5167162c' EP100 = '6a923121-36e1-4b5e-8cd6-90aceca9b5cf' @@ -66,12 +67,12 @@ DEVICE_R1 = json_device_packetrouter_disabled( DEVICE_R1_UUID, endpoints=DEVICE_R1_EPS, config_rules=DEVICE_R1_RULES) -DEVICE_R2_UUID = 'R2' +DEVICE_R2_UUID = '2fd2be23-5b20-414c-b1ea-2f16ae6eb425' DEVICE_R2_ID = json_device_id(DEVICE_R2_UUID) DEVICE_R2_EPS = [ - json_endpoint(DEVICE_R2_ID, 'EP1', '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), - json_endpoint(DEVICE_R2_ID, 'EP3', '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), - json_endpoint(DEVICE_R2_ID, 'EP100', '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), + json_endpoint(DEVICE_R2_ID, EP1, '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), + json_endpoint(DEVICE_R2_ID, EP3, '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), + json_endpoint(DEVICE_R2_ID, EP100, '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), ] DEVICE_R2_RULES = [ json_config_rule_set('dev/rsrc1/value', 'value4'), @@ -82,12 +83,12 @@ DEVICE_R2 = json_device_packetrouter_disabled( DEVICE_R2_UUID, endpoints=DEVICE_R2_EPS, config_rules=DEVICE_R2_RULES) -DEVICE_R3_UUID = 'R3' +DEVICE_R3_UUID = '3e71a251-2218-42c5-b4b8-de7760c0d9b3' DEVICE_R3_ID = json_device_id(DEVICE_R3_UUID) DEVICE_R3_EPS = [ - json_endpoint(DEVICE_R3_ID, 'EP1', '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), - json_endpoint(DEVICE_R3_ID, 'EP2', '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), - json_endpoint(DEVICE_R3_ID, 'EP100', '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), + json_endpoint(DEVICE_R3_ID, EP2, '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), + json_endpoint(DEVICE_R3_ID, EP3, '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), + json_endpoint(DEVICE_R3_ID, EP100, '10G', topology_id=TOPOLOGY_ID, kpi_sample_types=PACKET_PORT_SAMPLE_TYPES), ] DEVICE_R3_RULES = [ json_config_rule_set('dev/rsrc1/value', 'value4'), @@ -99,29 +100,29 @@ DEVICE_R3 = json_device_packetrouter_disabled( # ----- Link ----------------------------------------------------------------------------------------------------------- -LINK_R1_R2_UUID = 'R1/EP2-R2/EP1' +LINK_R1_R2_UUID = 'c8f92eec-340e-4d31-8d7e-7074927dc889' LINK_R1_R2_ID = json_link_id(LINK_R1_R2_UUID) LINK_R1_R2_EPIDS = [ - json_endpoint_id(DEVICE_R1_ID, 'EP2', topology_id=TOPOLOGY_ID), - json_endpoint_id(DEVICE_R2_ID, 'EP1', topology_id=TOPOLOGY_ID), + json_endpoint_id(DEVICE_R1_ID, EP2, topology_id=TOPOLOGY_ID), + json_endpoint_id(DEVICE_R2_ID, EP1, topology_id=TOPOLOGY_ID), ] LINK_R1_R2 = json_link(LINK_R1_R2_UUID, LINK_R1_R2_EPIDS) -LINK_R2_R3_UUID = 'R2/EP3-R3/EP2' +LINK_R2_R3_UUID = 'f9e3539a-d8f9-4737-b4b4-cacf7f90aa5d' LINK_R2_R3_ID = json_link_id(LINK_R2_R3_UUID) LINK_R2_R3_EPIDS = [ - json_endpoint_id(DEVICE_R2_ID, 'EP3', topology_id=TOPOLOGY_ID), - json_endpoint_id(DEVICE_R3_ID, 'EP2', topology_id=TOPOLOGY_ID), + json_endpoint_id(DEVICE_R2_ID, EP3, topology_id=TOPOLOGY_ID), + json_endpoint_id(DEVICE_R3_ID, EP2, topology_id=TOPOLOGY_ID), ] LINK_R2_R3 = json_link(LINK_R2_R3_UUID, LINK_R2_R3_EPIDS) -LINK_R1_R3_UUID = 'R1/EP3-R3/EP1' +LINK_R1_R3_UUID = '1f1a988c-47a9-41b2-afd9-ebd6d434a0b4' LINK_R1_R3_ID = json_link_id(LINK_R1_R3_UUID) LINK_R1_R3_EPIDS = [ - json_endpoint_id(DEVICE_R1_ID, 'EP3', topology_id=TOPOLOGY_ID), - json_endpoint_id(DEVICE_R3_ID, 'EP1', topology_id=TOPOLOGY_ID), + json_endpoint_id(DEVICE_R1_ID, EP3, topology_id=TOPOLOGY_ID), + json_endpoint_id(DEVICE_R3_ID, EP1, topology_id=TOPOLOGY_ID), ] LINK_R1_R3 = json_link(LINK_R1_R3_UUID, LINK_R1_R3_EPIDS) -- GitLab From 1b2eef22feb1ec33fda9c1b33580f7dce0a63a19 Mon Sep 17 00:00:00 2001 From: cmanso Date: Sun, 11 Dec 2022 23:43:52 +0100 Subject: [PATCH 077/353] Update scalability --- .../service/database/ConstraintModel.py | 310 ++++++++++-------- src/context/service/database/EndPointModel.py | 54 +-- src/context/service/database/ServiceModel.py | 61 ++-- .../grpc_server/ContextServiceServicerImpl.py | 207 ++++++++---- src/context/tests/Objects.py | 10 +- src/context/tests/test_unitary.py | 279 ++++++++-------- 6 files changed, 528 insertions(+), 393 deletions(-) diff --git a/src/context/service/database/ConstraintModel.py b/src/context/service/database/ConstraintModel.py index a35ec250d..c5ed7504d 100644 --- a/src/context/service/database/ConstraintModel.py +++ b/src/context/service/database/ConstraintModel.py @@ -13,91 +13,122 @@ # limitations under the License. import logging, operator -from enum import Enum from typing import Dict, List, Optional, Tuple, Type, Union -from common.orm.Database import Database from common.orm.HighLevel import get_object, get_or_create_object, update_or_create_object from common.orm.backend.Tools import key_to_str -from common.orm.fields.BooleanField import BooleanField -from common.orm.fields.EnumeratedField import EnumeratedField -from common.orm.fields.FloatField import FloatField -from common.orm.fields.ForeignKeyField import ForeignKeyField -from common.orm.fields.IntegerField import IntegerField -from common.orm.fields.PrimaryKeyField import PrimaryKeyField -from common.orm.fields.StringField import StringField -from common.orm.model.Model import Model from common.proto.context_pb2 import Constraint from common.tools.grpc.Tools import grpc_message_to_json_string -from .EndPointModel import EndPointModel, get_endpoint +from .EndPointModel import EndPointModel from .Tools import fast_hasher, remove_dict_key +from sqlalchemy import Column, ForeignKey, String, Float, CheckConstraint, Integer, Boolean, Enum +from sqlalchemy.dialects.postgresql import UUID +from context.service.database.Base import Base +import enum LOGGER = logging.getLogger(__name__) -class ConstraintsModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - def delete(self) -> None: - db_constraint_pks = self.references(ConstraintModel) - for pk,_ in db_constraint_pks: ConstraintModel(self.database, pk).delete() - super().delete() +class ConstraintsModel(Base): # pylint: disable=abstract-method + __tablename__ = 'Constraints' + constraints_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) - def dump(self) -> List[Dict]: - db_constraint_pks = self.references(ConstraintModel) - constraints = [ConstraintModel(self.database, pk).dump(include_position=True) for pk,_ in db_constraint_pks] + @staticmethod + def main_pk_name(): + return 'constraints_uuid' + + + def dump(self, constraints) -> List[Dict]: constraints = sorted(constraints, key=operator.itemgetter('position')) return [remove_dict_key(constraint, 'position') for constraint in constraints] -class ConstraintCustomModel(Model): # pylint: disable=abstract-method - constraint_type = StringField(required=True, allow_empty=False) - constraint_value = StringField(required=True, allow_empty=False) + +class ConstraintCustomModel(Base): # pylint: disable=abstract-method + __tablename__ = 'ConstraintCustom' + constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) + constraint_type = Column(String, nullable=False) + constraint_value = Column(String, nullable=False) + + @staticmethod + def main_pk_name(): + return 'constraint_uuid' + def dump(self) -> Dict: # pylint: disable=arguments-differ return {'custom': {'constraint_type': self.constraint_type, 'constraint_value': self.constraint_value}} + Union_ConstraintEndpoint = Union[ 'ConstraintEndpointLocationGpsPositionModel', 'ConstraintEndpointLocationRegionModel', 'ConstraintEndpointPriorityModel' ] -def dump_endpoint_id(endpoint_constraint : Union_ConstraintEndpoint): - db_endpoints_pks = list(endpoint_constraint.references(EndPointModel)) - num_endpoints = len(db_endpoints_pks) - if num_endpoints != 1: - raise Exception('Wrong number({:d}) of associated Endpoints with constraint'.format(num_endpoints)) - db_endpoint = EndPointModel(endpoint_constraint.database, db_endpoints_pks[0]) - return db_endpoint.dump_id() - -class ConstraintEndpointLocationRegionModel(Model): # pylint: disable=abstract-method - endpoint_fk = ForeignKeyField(EndPointModel) - region = StringField(required=True, allow_empty=False) - def dump(self) -> Dict: # pylint: disable=arguments-differ - return {'endpoint_location': {'endpoint_id': dump_endpoint_id(self), 'region': self.region}} -class ConstraintEndpointLocationGpsPositionModel(Model): # pylint: disable=abstract-method - endpoint_fk = ForeignKeyField(EndPointModel) - latitude = FloatField(required=True, min_value=-90.0, max_value=90.0) - longitude = FloatField(required=True, min_value=-180.0, max_value=180.0) +# def dump_endpoint_id(endpoint_constraint: Union_ConstraintEndpoint): +# db_endpoints_pks = list(endpoint_constraint.references(EndPointModel)) +# num_endpoints = len(db_endpoints_pks) +# if num_endpoints != 1: +# raise Exception('Wrong number({:d}) of associated Endpoints with constraint'.format(num_endpoints)) +# db_endpoint = EndPointModel(endpoint_constraint.database, db_endpoints_pks[0]) +# return db_endpoint.dump_id() - def dump(self) -> Dict: # pylint: disable=arguments-differ - gps_position = {'latitude': self.latitude, 'longitude': self.longitude} - return {'endpoint_location': {'endpoint_id': dump_endpoint_id(self), 'gps_position': gps_position}} -class ConstraintEndpointPriorityModel(Model): # pylint: disable=abstract-method - endpoint_fk = ForeignKeyField(EndPointModel) - priority = FloatField(required=True) +class ConstraintEndpointLocationRegionModel(Base): # pylint: disable=abstract-method + __tablename__ = 'ConstraintEndpointLocationRegion' + constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) + endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid")) + region = Column(String, nullable=False) + + @staticmethod + def main_pk_name(): + return 'constraint_uuid' + + def dump(self, endpoint) -> Dict: # pylint: disable=arguments-differ + return {'endpoint_location': {'endpoint_id': endpoint.dump_id(), 'region': self.region}} - def dump(self) -> Dict: # pylint: disable=arguments-differ - return {'endpoint_priority': {'endpoint_id': dump_endpoint_id(self), 'priority': self.priority}} -class ConstraintSlaAvailabilityModel(Model): # pylint: disable=abstract-method - num_disjoint_paths = IntegerField(required=True, min_value=1) - all_active = BooleanField(required=True) +class ConstraintEndpointLocationGpsPositionModel(Base): # pylint: disable=abstract-method + __tablename__ = 'ConstraintEndpointLocationGpsPosition' + constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) + endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid")) + latitude = Column(Float, CheckConstraint('latitude > -90.0 AND latitude < 90.0'), nullable=False) + longitude = Column(Float, CheckConstraint('longitude > -90.0 AND longitude < 90.0'), nullable=False) + + def dump(self, endpoint) -> Dict: # pylint: disable=arguments-differ + gps_position = {'latitude': self.latitude, 'longitude': self.longitude} + return {'endpoint_location': {'endpoint_id': endpoint.dump_id(), 'gps_position': gps_position}} + + +class ConstraintEndpointPriorityModel(Base): # pylint: disable=abstract-method + __tablename__ = 'ConstraintEndpointPriority' + constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) + endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid")) + # endpoint_fk = ForeignKeyField(EndPointModel) + # priority = FloatField(required=True) + priority = Column(Float, nullable=False) + @staticmethod + def main_pk_name(): + return 'constraint_uuid' + + def dump(self, endpoint) -> Dict: # pylint: disable=arguments-differ + return {'endpoint_priority': {'endpoint_id': endpoint.dump_id(), 'priority': self.priority}} + + +class ConstraintSlaAvailabilityModel(Base): # pylint: disable=abstract-method + __tablename__ = 'ConstraintSlaAvailability' + constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) + # num_disjoint_paths = IntegerField(required=True, min_value=1) + num_disjoint_paths = Column(Integer, CheckConstraint('num_disjoint_paths > 1'), nullable=False) + # all_active = BooleanField(required=True) + all_active = Column(Boolean, nullable=False) + @staticmethod + def main_pk_name(): + return 'constraint_uuid' def dump(self) -> Dict: # pylint: disable=arguments-differ return {'sla_availability': {'num_disjoint_paths': self.num_disjoint_paths, 'all_active': self.all_active}} # enum values should match name of field in ConstraintModel -class ConstraintKindEnum(Enum): +class ConstraintKindEnum(enum.Enum): CUSTOM = 'custom' ENDPOINT_LOCATION_REGION = 'ep_loc_region' ENDPOINT_LOCATION_GPSPOSITION = 'ep_loc_gpspos' @@ -109,41 +140,56 @@ Union_SpecificConstraint = Union[ ConstraintEndpointPriorityModel, ConstraintSlaAvailabilityModel, ] -class ConstraintModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - constraints_fk = ForeignKeyField(ConstraintsModel) - kind = EnumeratedField(ConstraintKindEnum) - position = IntegerField(min_value=0, required=True) - constraint_custom_fk = ForeignKeyField(ConstraintCustomModel, required=False) - constraint_ep_loc_region_fk = ForeignKeyField(ConstraintEndpointLocationRegionModel, required=False) - constraint_ep_loc_gpspos_fk = ForeignKeyField(ConstraintEndpointLocationGpsPositionModel, required=False) - constraint_ep_priority_fk = ForeignKeyField(ConstraintEndpointPriorityModel, required=False) - constraint_sla_avail_fk = ForeignKeyField(ConstraintSlaAvailabilityModel, required=False) - - def delete(self) -> None: - field_name = 'constraint_{:s}_fk'.format(str(self.kind.value)) - specific_fk_value : Optional[ForeignKeyField] = getattr(self, field_name, None) - if specific_fk_value is None: - raise Exception('Unable to find constraint key for field_name({:s})'.format(field_name)) - specific_fk_class = getattr(ConstraintModel, field_name, None) - foreign_model_class : Model = specific_fk_class.foreign_model - super().delete() - get_object(self.database, foreign_model_class, str(specific_fk_value)).delete() +class ConstraintModel(Base): # pylint: disable=abstract-method + __tablename__ = 'Constraint' + # pk = PrimaryKeyField() + # constraints_fk = ForeignKeyField(ConstraintsModel) + constraints_uuid = Column(UUID(as_uuid=False), ForeignKey("Constraints.constraints_uuid"), primary_key=True) + # kind = EnumeratedField(ConstraintKindEnum) + kind = Column(Enum(ConstraintKindEnum, create_constraint=False, native_enum=False)) + # position = IntegerField(min_value=0, required=True) + position = Column(Integer, CheckConstraint('position >= 0'), nullable=False) + # constraint_custom_fk = ForeignKeyField(ConstraintCustomModel, required=False) + constraint_custom = Column(UUID(as_uuid=False), ForeignKey("ConstraintCustom.constraint_uuid")) + # constraint_ep_loc_region_fk = ForeignKeyField(ConstraintEndpointLocationRegionModel, required=False) + constraint_ep_loc_region = Column(UUID(as_uuid=False), ForeignKey("ConstraintEndpointLocationRegion.constraint_uuid")) + # constraint_ep_loc_gpspos_fk = ForeignKeyField(ConstraintEndpointLocationGpsPositionModel, required=False) + constraint_ep_loc_gpspos = Column(UUID(as_uuid=False), ForeignKey("ConstraintEndpointLocationGpsPosition.constraint_uuid")) + # constraint_ep_priority_fk = ForeignKeyField(ConstraintEndpointPriorityModel, required=False) + constraint_ep_priority = Column(UUID(as_uuid=False), ForeignKey("ConstraintEndpointPriority.constraint_uuid"),) + # constraint_sla_avail_fk = ForeignKeyField(ConstraintSlaAvailabilityModel, required=False) + constraint_sla_avail = Column(UUID(as_uuid=False), ForeignKey("ConstraintSlaAvailability.constraint_uuid")) + + @staticmethod + def main_pk_name(): + return 'constraint_uuid' + + # def delete(self) -> None: + # field_name = 'constraint_{:s}_fk'.format(str(self.kind.value)) + # specific_fk_value : Optional[ForeignKeyField] = getattr(self, field_name, None) + # if specific_fk_value is None: + # raise Exception('Unable to find constraint key for field_name({:s})'.format(field_name)) + # specific_fk_class = getattr(ConstraintModel, field_name, None) + # foreign_model_class : Model = specific_fk_class.foreign_model + # super().delete() + # get_object(self.database, foreign_model_class, str(specific_fk_value)).delete() def dump(self, include_position=True) -> Dict: # pylint: disable=arguments-differ - field_name = 'constraint_{:s}_fk'.format(str(self.kind.value)) - specific_fk_value : Optional[ForeignKeyField] = getattr(self, field_name, None) + field_name = 'constraint_{:s}'.format(str(self.kind.value)) + specific_fk_value = getattr(self, field_name, None) if specific_fk_value is None: raise Exception('Unable to find constraint key for field_name({:s})'.format(field_name)) specific_fk_class = getattr(ConstraintModel, field_name, None) - foreign_model_class : Model = specific_fk_class.foreign_model - constraint : Union_SpecificConstraint = get_object(self.database, foreign_model_class, str(specific_fk_value)) + foreign_model_class: Base = specific_fk_class.foreign_model + constraint: Union_SpecificConstraint = get_object(self.database, foreign_model_class, str(specific_fk_value)) result = constraint.dump() - if include_position: result['position'] = self.position + if include_position: + result['position'] = self.position return result Tuple_ConstraintSpecs = Tuple[Type, str, Dict, ConstraintKindEnum] -def parse_constraint_custom(database : Database, grpc_constraint) -> Tuple_ConstraintSpecs: + +def parse_constraint_custom(grpc_constraint) -> Tuple_ConstraintSpecs: constraint_class = ConstraintCustomModel str_constraint_id = grpc_constraint.custom.constraint_type constraint_data = { @@ -152,11 +198,11 @@ def parse_constraint_custom(database : Database, grpc_constraint) -> Tuple_Const } return constraint_class, str_constraint_id, constraint_data, ConstraintKindEnum.CUSTOM -def parse_constraint_endpoint_location(database : Database, grpc_constraint) -> Tuple_ConstraintSpecs: +def parse_constraint_endpoint_location(db_endpoint, grpc_constraint) -> Tuple_ConstraintSpecs: grpc_endpoint_id = grpc_constraint.endpoint_location.endpoint_id - str_endpoint_key, db_endpoint = get_endpoint(database, grpc_endpoint_id) + # str_endpoint_key, db_endpoint = get_endpoint(database, grpc_endpoint_id) - str_constraint_id = str_endpoint_key + str_constraint_id = db_endpoint.endpoint_uuid constraint_data = {'endpoint_fk': db_endpoint} grpc_location = grpc_constraint.endpoint_location.location @@ -174,18 +220,18 @@ def parse_constraint_endpoint_location(database : Database, grpc_constraint) -> MSG = 'Location kind {:s} in Constraint of kind endpoint_location is not implemented: {:s}' raise NotImplementedError(MSG.format(location_kind, grpc_message_to_json_string(grpc_constraint))) -def parse_constraint_endpoint_priority(database : Database, grpc_constraint) -> Tuple_ConstraintSpecs: +def parse_constraint_endpoint_priority(db_endpoint, grpc_constraint) -> Tuple_ConstraintSpecs: grpc_endpoint_id = grpc_constraint.endpoint_priority.endpoint_id - str_endpoint_key, db_endpoint = get_endpoint(database, grpc_endpoint_id) + # str_endpoint_key, db_endpoint = get_endpoint(database, grpc_endpoint_id) constraint_class = ConstraintEndpointPriorityModel - str_constraint_id = str_endpoint_key + str_constraint_id = db_endpoint.endpoint_uuid priority = grpc_constraint.endpoint_priority.priority constraint_data = {'endpoint_fk': db_endpoint, 'priority': priority} return constraint_class, str_constraint_id, constraint_data, ConstraintKindEnum.ENDPOINT_PRIORITY -def parse_constraint_sla_availability(database : Database, grpc_constraint) -> Tuple_ConstraintSpecs: +def parse_constraint_sla_availability(grpc_constraint) -> Tuple_ConstraintSpecs: constraint_class = ConstraintSlaAvailabilityModel str_constraint_id = '' constraint_data = { @@ -206,50 +252,50 @@ Union_ConstraintModel = Union[ ConstraintEndpointPriorityModel, ConstraintSlaAvailabilityModel ] -def set_constraint( - database : Database, db_constraints : ConstraintsModel, grpc_constraint : Constraint, position : int -) -> Tuple[Union_ConstraintModel, bool]: - grpc_constraint_kind = str(grpc_constraint.WhichOneof('constraint')) - - parser = CONSTRAINT_PARSERS.get(grpc_constraint_kind) - if parser is None: - raise NotImplementedError('Constraint of kind {:s} is not implemented: {:s}'.format( - grpc_constraint_kind, grpc_message_to_json_string(grpc_constraint))) - - # create specific constraint - constraint_class, str_constraint_id, constraint_data, constraint_kind = parser(database, grpc_constraint) - str_constraint_key_hash = fast_hasher(':'.join([constraint_kind.value, str_constraint_id])) - str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':') - result : Tuple[Union_ConstraintModel, bool] = update_or_create_object( - database, constraint_class, str_constraint_key, constraint_data) - db_specific_constraint, updated = result - - # create generic constraint - constraint_fk_field_name = 'constraint_{:s}_fk'.format(constraint_kind.value) - constraint_data = { - 'constraints_fk': db_constraints, 'position': position, 'kind': constraint_kind, - constraint_fk_field_name: db_specific_constraint - } - result : Tuple[ConstraintModel, bool] = update_or_create_object( - database, ConstraintModel, str_constraint_key, constraint_data) - db_constraint, updated = result - - return db_constraint, updated - -def set_constraints( - database : Database, db_parent_pk : str, constraints_name : str, grpc_constraints -) -> List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]]: - - str_constraints_key = key_to_str([db_parent_pk, constraints_name], separator=':') - result : Tuple[ConstraintsModel, bool] = get_or_create_object(database, ConstraintsModel, str_constraints_key) - db_constraints, created = result - - db_objects = [(db_constraints, created)] - - for position,grpc_constraint in enumerate(grpc_constraints): - result : Tuple[ConstraintModel, bool] = set_constraint( - database, db_constraints, grpc_constraint, position) - db_constraint, updated = result - db_objects.append((db_constraint, updated)) - - return db_objects +# def set_constraint( +# db_constraints : ConstraintsModel, grpc_constraint : Constraint, position : int +# ) -> Tuple[Union_ConstraintModel, bool]: +# grpc_constraint_kind = str(grpc_constraint.WhichOneof('constraint')) +# +# parser = CONSTRAINT_PARSERS.get(grpc_constraint_kind) +# if parser is None: +# raise NotImplementedError('Constraint of kind {:s} is not implemented: {:s}'.format( +# grpc_constraint_kind, grpc_message_to_json_string(grpc_constraint))) +# +# # create specific constraint +# constraint_class, str_constraint_id, constraint_data, constraint_kind = parser(database, grpc_constraint) +# str_constraint_key_hash = fast_hasher(':'.join([constraint_kind.value, str_constraint_id])) +# str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':') +# result : Tuple[Union_ConstraintModel, bool] = update_or_create_object( +# database, constraint_class, str_constraint_key, constraint_data) +# db_specific_constraint, updated = result +# +# # create generic constraint +# constraint_fk_field_name = 'constraint_{:s}_fk'.format(constraint_kind.value) +# constraint_data = { +# 'constraints_fk': db_constraints, 'position': position, 'kind': constraint_kind, +# constraint_fk_field_name: db_specific_constraint +# } +# result : Tuple[ConstraintModel, bool] = update_or_create_object( +# database, ConstraintModel, str_constraint_key, constraint_data) +# db_constraint, updated = result +# +# return db_constraint, updated +# +# def set_constraints( +# database : Database, db_parent_pk : str, constraints_name : str, grpc_constraints +# ) -> List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]]: +# +# str_constraints_key = key_to_str([db_parent_pk, constraints_name], separator=':') +# result : Tuple[ConstraintsModel, bool] = get_or_create_object(database, ConstraintsModel, str_constraints_key) +# db_constraints, created = result +# +# db_objects = [(db_constraints, created)] +# +# for position,grpc_constraint in enumerate(grpc_constraints): +# result : Tuple[ConstraintModel, bool] = set_constraint( +# database, db_constraints, grpc_constraint, position) +# db_constraint, updated = result +# db_objects.append((db_constraint, updated)) +# +# return db_objects diff --git a/src/context/service/database/EndPointModel.py b/src/context/service/database/EndPointModel.py index fb2c9d26a..540453970 100644 --- a/src/context/service/database/EndPointModel.py +++ b/src/context/service/database/EndPointModel.py @@ -99,30 +99,30 @@ def set_kpi_sample_types(database : Database, db_endpoint : EndPointModel, grpc_ db_endpoint_kpi_sample_type.kpi_sample_type = orm_kpi_sample_type db_endpoint_kpi_sample_type.save() """ -def get_endpoint( - database : Database, grpc_endpoint_id : EndPointId, - validate_topology_exists : bool = True, validate_device_in_topology : bool = True -) -> Tuple[str, EndPointModel]: - endpoint_uuid = grpc_endpoint_id.endpoint_uuid.uuid - endpoint_device_uuid = grpc_endpoint_id.device_id.device_uuid.uuid - endpoint_topology_uuid = grpc_endpoint_id.topology_id.topology_uuid.uuid - endpoint_topology_context_uuid = grpc_endpoint_id.topology_id.context_id.context_uuid.uuid - str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) - - if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: - # check topology exists - str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) - if validate_topology_exists: - from .TopologyModel import TopologyModel - get_object(database, TopologyModel, str_topology_key) - - # check device is in topology - str_topology_device_key = key_to_str([str_topology_key, endpoint_device_uuid], separator='--') - if validate_device_in_topology: - from .RelationModels import TopologyDeviceModel - get_object(database, TopologyDeviceModel, str_topology_device_key) - - str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') - - db_endpoint : EndPointModel = get_object(database, EndPointModel, str_endpoint_key) - return str_endpoint_key, db_endpoint +# def get_endpoint( +# database : Database, grpc_endpoint_id : EndPointId, +# validate_topology_exists : bool = True, validate_device_in_topology : bool = True +# ) -> Tuple[str, EndPointModel]: +# endpoint_uuid = grpc_endpoint_id.endpoint_uuid.uuid +# endpoint_device_uuid = grpc_endpoint_id.device_id.device_uuid.uuid +# endpoint_topology_uuid = grpc_endpoint_id.topology_id.topology_uuid.uuid +# endpoint_topology_context_uuid = grpc_endpoint_id.topology_id.context_id.context_uuid.uuid +# str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) +# +# if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: +# # check topology exists +# str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) +# if validate_topology_exists: +# from .TopologyModel import TopologyModel +# get_object(database, TopologyModel, str_topology_key) +# +# # check device is in topology +# str_topology_device_key = key_to_str([str_topology_key, endpoint_device_uuid], separator='--') +# if validate_device_in_topology: +# from .RelationModels import TopologyDeviceModel +# get_object(database, TopologyDeviceModel, str_topology_device_key) +# +# str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') +# +# db_endpoint : EndPointModel = get_object(database, EndPointModel, str_endpoint_key) +# return str_endpoint_key, db_endpoint diff --git a/src/context/service/database/ServiceModel.py b/src/context/service/database/ServiceModel.py index a5223d615..8f358be52 100644 --- a/src/context/service/database/ServiceModel.py +++ b/src/context/service/database/ServiceModel.py @@ -13,7 +13,7 @@ # limitations under the License. import functools, logging, operator -from sqlalchemy import Column, ForeignKey, String, Enum +from sqlalchemy import Column, Enum, ForeignKey from typing import Dict, List from common.orm.HighLevel import get_related_objects from common.proto.context_pb2 import ServiceStatusEnum, ServiceTypeEnum @@ -21,12 +21,12 @@ from .ConfigModel import ConfigModel from .ConstraintModel import ConstraintsModel from .ContextModel import ContextModel from .Tools import grpc_to_enum -from sqlalchemy import Column, ForeignKey from sqlalchemy.dialects.postgresql import UUID from context.service.database.Base import Base +import enum LOGGER = logging.getLogger(__name__) -class ORM_ServiceTypeEnum(Enum): +class ORM_ServiceTypeEnum(enum.Enum): UNKNOWN = ServiceTypeEnum.SERVICETYPE_UNKNOWN L3NM = ServiceTypeEnum.SERVICETYPE_L3NM L2NM = ServiceTypeEnum.SERVICETYPE_L2NM @@ -35,7 +35,7 @@ class ORM_ServiceTypeEnum(Enum): grpc_to_enum__service_type = functools.partial( grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum) -class ORM_ServiceStatusEnum(Enum): +class ORM_ServiceStatusEnum(enum.Enum): UNDEFINED = ServiceStatusEnum.SERVICESTATUS_UNDEFINED PLANNED = ServiceStatusEnum.SERVICESTATUS_PLANNED ACTIVE = ServiceStatusEnum.SERVICESTATUS_ACTIVE @@ -47,24 +47,35 @@ grpc_to_enum__service_status = functools.partial( class ServiceModel(Base): __tablename__ = 'Service' + # pk = PrimaryKeyField() + # context_fk = ForeignKeyField(ContextModel) + context_uuid = Column(UUID(as_uuid=False), ForeignKey("Context.context_uuid")) + # service_uuid = StringField(required=True, allow_empty=False) service_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) + # service_type = EnumeratedField(ORM_ServiceTypeEnum, required=True) service_type = Column(Enum(ORM_ServiceTypeEnum, create_constraint=False, native_enum=False, allow_empty=False)) - # service_constraints = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid", ondelete='SET NULL')) - # context_fk = ForeignKeyField(ContextModel) + # service_constraints_fk = ForeignKeyField(ConstraintsModel) + service_constraints = Column(UUID(as_uuid=False), ForeignKey("Constraints.constraints_uuid")) + # service_status = EnumeratedField(ORM_ServiceStatusEnum, required=True) service_status = Column(Enum(ORM_ServiceStatusEnum, create_constraint=False, native_enum=False, allow_empty=False)) # service_config_fk = ForeignKeyField(ConfigModel) + service_config = Column(UUID(as_uuid=False), ForeignKey("Config.config_uuid")) - def delete(self) -> None: - #pylint: disable=import-outside-toplevel - from .RelationModels import ServiceEndPointModel - - for db_service_endpoint_pk,_ in self.references(ServiceEndPointModel): - ServiceEndPointModel(self.database, db_service_endpoint_pk).delete() + # def delete(self) -> None: + # #pylint: disable=import-outside-toplevel + # from .RelationModels import ServiceEndPointModel + # + # for db_service_endpoint_pk,_ in self.references(ServiceEndPointModel): + # ServiceEndPointModel(self.database, db_service_endpoint_pk).delete() + # + # super().delete() + # + # ConfigModel(self.database, self.service_config_fk).delete() + # ConstraintsModel(self.database, self.service_constraints_fk).delete() - super().delete() + def main_pk_name(self): + return 'context_uuid' - ConfigModel(self.database, self.service_config_fk).delete() - ConstraintsModel(self.database, self.service_constraints_fk).delete() def dump_id(self) -> Dict: context_id = ContextModel(self.database, self.context_fk).dump_id() @@ -73,10 +84,10 @@ class ServiceModel(Base): 'service_uuid': {'uuid': self.service_uuid}, } - def dump_endpoint_ids(self) -> List[Dict]: - from .RelationModels import ServiceEndPointModel # pylint: disable=import-outside-toplevel - db_endpoints = get_related_objects(self, ServiceEndPointModel, 'endpoint_fk') - return [db_endpoint.dump_id() for db_endpoint in sorted(db_endpoints, key=operator.attrgetter('pk'))] + # def dump_endpoint_ids(self, endpoints) -> List[Dict]: + # from .RelationModels import ServiceEndPointModel # pylint: disable=import-outside-toplevel + # db_endpoints = get_related_objects(self, ServiceEndPointModel, 'endpoint_fk') + # return [db_endpoint.dump_id() for db_endpoint in sorted(db_endpoints, key=operator.attrgetter('pk'))] def dump_constraints(self) -> List[Dict]: return ConstraintsModel(self.database, self.service_constraints_fk).dump() @@ -85,14 +96,16 @@ class ServiceModel(Base): return ConfigModel(self.database, self.service_config_fk).dump() def dump( # pylint: disable=arguments-differ - self, include_endpoint_ids=True, include_constraints=True, include_config_rules=True - ) -> Dict: + self, endpoint_ids=True, constraints=True, config_rules=True) -> Dict: result = { 'service_id': self.dump_id(), 'service_type': self.service_type.value, 'service_status': {'service_status': self.service_status.value}, } - if include_endpoint_ids: result['service_endpoint_ids'] = self.dump_endpoint_ids() - if include_constraints: result['service_constraints'] = self.dump_constraints() - if include_config_rules: result.setdefault('service_config', {})['config_rules'] = self.dump_config() + if endpoint_ids: + result['service_endpoint_ids'] = self.dump_endpoint_ids() + if constraints: + result['service_constraints'] = self.dump_constraints() + if config_rules: + result.setdefault('service_config', {})['config_rules'] = self.dump_config() return result diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py index 264ae3198..98c961007 100644 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py @@ -17,6 +17,7 @@ import grpc, json, logging, operator, threading from typing import Iterator, List, Set, Tuple, Union from common.message_broker.MessageBroker import MessageBroker from context.service.Database import Database +from common.tools.grpc.Tools import grpc_message_to_json_string from common.proto.context_pb2 import ( Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, @@ -27,7 +28,7 @@ from common.proto.context_pb2 import ( Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList, Slice, SliceEvent, SliceId, SliceIdList, SliceList, Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList, - ConfigActionEnum) + ConfigActionEnum, Constraint) from common.proto.context_pb2_grpc import ContextServiceServicer from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException @@ -60,6 +61,8 @@ from context.service.database.Events import notify_event from context.service.database.EndPointModel import EndPointModel from context.service.database.EndPointModel import KpiSampleTypeModel from context.service.database.LinkModel import LinkModel +from context.service.database.ServiceModel import ServiceModel +from context.service.database.ConstraintModel import ConstraintModel, ConstraintsModel, Union_ConstraintModel, CONSTRAINT_PARSERS from context.service.database.RelationModels import (TopologyDeviceModel, TopologyLinkModel, LinkEndPointModel) from .Constants import ( @@ -640,87 +643,153 @@ class ContextServiceServicerImpl(ContextServiceServicer): @safe_and_metered_rpc_method(METRICS, LOGGER) def ListServiceIds(self, request: ContextId, context : grpc.ServicerContext) -> ServiceIdList: - with self.lock: - db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid) - db_services : Set[ServiceModel] = get_related_objects(db_context, ServiceModel) - db_services = sorted(db_services, key=operator.attrgetter('pk')) + context_uuid = request.context_uuid.uuid + + with self.session() as session: + db_services = session.query(ServiceModel).filter_by(context_uuid=context_uuid).all() return ServiceIdList(service_ids=[db_service.dump_id() for db_service in db_services]) @safe_and_metered_rpc_method(METRICS, LOGGER) def ListServices(self, request: ContextId, context : grpc.ServicerContext) -> ServiceList: - with self.lock: - db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid) - db_services : Set[ServiceModel] = get_related_objects(db_context, ServiceModel) - db_services = sorted(db_services, key=operator.attrgetter('pk')) - return ServiceList(services=[db_service.dump() for db_service in db_services]) + context_uuid = request.context_uuid.uuid - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetService(self, request: ServiceId, context : grpc.ServicerContext) -> Service: - with self.lock: - str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid]) - db_service : ServiceModel = get_object(self.database, ServiceModel, str_key) - return Service(**db_service.dump( - include_endpoint_ids=True, include_constraints=True, include_config_rules=True)) + with self.session() as session: + db_services = session.query(ServiceModel).filter_by(context_uuid=context_uuid).all() + return ServiceList(services=[db_service.dump() for db_service in db_services]) - @safe_and_metered_rpc_method(METRICS, LOGGER) - def SetService(self, request: Service, context : grpc.ServicerContext) -> ServiceId: - with self.lock: - context_uuid = request.service_id.context_id.context_uuid.uuid - db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) - for i,endpoint_id in enumerate(request.service_endpoint_ids): - endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid - if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: - raise InvalidArgumentException( - 'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), - endpoint_topology_context_uuid, - ['should be == {:s}({:s})'.format( - 'request.service_id.context_id.context_uuid.uuid', context_uuid)]) - service_uuid = request.service_id.service_uuid.uuid - str_service_key = key_to_str([context_uuid, service_uuid]) + @safe_and_metered_rpc_method(METRICS, LOGGER) + def GetService(self, request: ServiceId, context : grpc.ServicerContext) -> Service: + service_uuid = request.service_uuid.uuid + with self.session() as session: + result = session.query(ServiceModel).filter_by(service_uuid=service_uuid).one_or_none() - constraints_result = set_constraints( - self.database, str_service_key, 'constraints', request.service_constraints) - db_constraints = constraints_result[0][0] + if not result: + raise NotFoundException(ServiceModel.__name__.replace('Model', ''), service_uuid) - config_rules = grpc_config_rules_to_raw(request.service_config.config_rules) - running_config_result = update_config(self.database, str_service_key, 'running', config_rules) - db_running_config = running_config_result[0][0] + return Service(**result.dump()) - result : Tuple[ServiceModel, bool] = update_or_create_object(self.database, ServiceModel, str_service_key, { - 'context_fk' : db_context, - 'service_uuid' : service_uuid, - 'service_type' : grpc_to_enum__service_type(request.service_type), - 'service_constraints_fk': db_constraints, - 'service_status' : grpc_to_enum__service_status(request.service_status.service_status), - 'service_config_fk' : db_running_config, - }) - db_service, updated = result + def set_constraint(self, db_constraints: ConstraintsModel, grpc_constraint: Constraint, position: int + ) -> Tuple[Union_ConstraintModel, bool]: + with self.session() as session: - for i,endpoint_id in enumerate(request.service_endpoint_ids): - endpoint_uuid = endpoint_id.endpoint_uuid.uuid - endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid - endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid - endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid + grpc_constraint_kind = str(grpc_constraint.WhichOneof('constraint')) + + parser = CONSTRAINT_PARSERS.get(grpc_constraint_kind) + if parser is None: + raise NotImplementedError('Constraint of kind {:s} is not implemented: {:s}'.format( + grpc_constraint_kind, grpc_message_to_json_string(grpc_constraint))) + + # create specific constraint + constraint_class, str_constraint_id, constraint_data, constraint_kind = parser(grpc_constraint) + LOGGER.info('str_constraint_id: {}'.format(str_constraint_id)) + # str_constraint_key_hash = fast_hasher(':'.join([constraint_kind.value, str_constraint_id])) + # str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':') + + # result : Tuple[Union_ConstraintModel, bool] = update_or_create_object( + # database, constraint_class, str_constraint_key, constraint_data) + constraint_data[constraint_class.main_pk_name()] = str_constraint_id + db_new_constraint = constraint_class(**constraint_data) + result: Tuple[Union_ConstraintModel, bool] = self.database.create_or_update(db_new_constraint) + db_specific_constraint, updated = result + + # create generic constraint + # constraint_fk_field_name = 'constraint_uuid'.format(constraint_kind.value) + constraint_data = { + 'constraint_uuid': db_constraints.constraint_uuid, 'position': position, 'kind': constraint_kind + } - str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) - if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: - str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) - str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') + db_new_constraint = ConstraintModel(**constraint_data) + result: Tuple[Union_ConstraintModel, bool] = self.database.create_or_update(db_new_constraint) + db_constraint, updated = result - db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) + return db_constraint, updated - str_service_endpoint_key = key_to_str([service_uuid, str_endpoint_key], separator='--') - result : Tuple[ServiceEndPointModel, bool] = get_or_create_object( - self.database, ServiceEndPointModel, str_service_endpoint_key, { - 'service_fk': db_service, 'endpoint_fk': db_endpoint}) - #db_service_endpoint, service_endpoint_created = result + def set_constraints(self, service_uuid: str, constraints_name : str, grpc_constraints + ) -> List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]]: + with self.session() as session: + # str_constraints_key = key_to_str([db_parent_pk, constraints_name], separator=':') + # result : Tuple[ConstraintsModel, bool] = get_or_create_object(database, ConstraintsModel, str_constraints_key) + result = session.query(ConstraintsModel).filter_by(constraints_uuid=service_uuid).one_or_none() + created = None + if result: + created = True + session.query(ConstraintsModel).filter_by(constraints_uuid=service_uuid).one_or_none() + db_constraints = ConstraintsModel(constraints_uuid=service_uuid) + session.add(db_constraints) + + db_objects = [(db_constraints, created)] + + for position,grpc_constraint in enumerate(grpc_constraints): + result : Tuple[ConstraintModel, bool] = self.set_constraint( + db_constraints, grpc_constraint, position) + db_constraint, updated = result + db_objects.append((db_constraint, updated)) + + return db_objects - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_service_id = db_service.dump_id() - notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id}) - return ServiceId(**dict_service_id) + @safe_and_metered_rpc_method(METRICS, LOGGER) + def SetService(self, request: Service, context : grpc.ServicerContext) -> ServiceId: + with self.lock: + with self.session() as session: + + context_uuid = request.service_id.context_id.context_uuid.uuid + # db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) + db_context = session.query(ContextModel).filter_by(context_uuid=context_uuid).one_or_none() + + for i,endpoint_id in enumerate(request.service_endpoint_ids): + endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid + if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: + raise InvalidArgumentException( + 'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), + endpoint_topology_context_uuid, + ['should be == {:s}({:s})'.format( + 'request.service_id.context_id.context_uuid.uuid', context_uuid)]) + + service_uuid = request.service_id.service_uuid.uuid + # str_service_key = key_to_str([context_uuid, service_uuid]) + + constraints_result = self.set_constraints(service_uuid, 'constraints', request.service_constraints) + db_constraints = constraints_result[0][0] + + config_rules = grpc_config_rules_to_raw(request.service_config.config_rules) + running_config_result = update_config(self.database, str_service_key, 'running', config_rules) + db_running_config = running_config_result[0][0] + + result : Tuple[ServiceModel, bool] = update_or_create_object(self.database, ServiceModel, str_service_key, { + 'context_fk' : db_context, + 'service_uuid' : service_uuid, + 'service_type' : grpc_to_enum__service_type(request.service_type), + 'service_constraints_fk': db_constraints, + 'service_status' : grpc_to_enum__service_status(request.service_status.service_status), + 'service_config_fk' : db_running_config, + }) + db_service, updated = result + + for i,endpoint_id in enumerate(request.service_endpoint_ids): + endpoint_uuid = endpoint_id.endpoint_uuid.uuid + endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid + endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid + endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid + + str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) + if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: + str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) + str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') + + db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) + + str_service_endpoint_key = key_to_str([service_uuid, str_endpoint_key], separator='--') + result : Tuple[ServiceEndPointModel, bool] = get_or_create_object( + self.database, ServiceEndPointModel, str_service_endpoint_key, { + 'service_fk': db_service, 'endpoint_fk': db_endpoint}) + #db_service_endpoint, service_endpoint_created = result + + event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE + dict_service_id = db_service.dump_id() + notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id}) + return ServiceId(**dict_service_id) @safe_and_metered_rpc_method(METRICS, LOGGER) def RemoveService(self, request: ServiceId, context : grpc.ServicerContext) -> Empty: @@ -743,7 +812,6 @@ class ContextServiceServicerImpl(ContextServiceServicer): for message in self.messagebroker.consume({TOPIC_SERVICE}, consume_timeout=CONSUME_TIMEOUT): yield ServiceEvent(**json.loads(message.content)) - """ # ----- Slice ---------------------------------------------------------------------------------------------------- @@ -881,6 +949,10 @@ class ContextServiceServicerImpl(ContextServiceServicer): @safe_and_metered_rpc_method(METRICS, LOGGER) def ListConnectionIds(self, request: ServiceId, context : grpc.ServicerContext) -> ConnectionIdList: + with self.session() as session: + result = session.query(DeviceModel).all() + return DeviceIdList(device_ids=[device.dump_id() for device in result]) + with self.lock: str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid]) db_service : ServiceModel = get_object(self.database, ServiceModel, str_key) @@ -960,4 +1032,3 @@ class ContextServiceServicerImpl(ContextServiceServicer): def GetConnectionEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ConnectionEvent]: for message in self.messagebroker.consume({TOPIC_CONNECTION}, consume_timeout=CONSUME_TIMEOUT): yield ConnectionEvent(**json.loads(message.content)) - """ \ No newline at end of file diff --git a/src/context/tests/Objects.py b/src/context/tests/Objects.py index a2aebdd96..a0c4f8232 100644 --- a/src/context/tests/Objects.py +++ b/src/context/tests/Objects.py @@ -128,11 +128,11 @@ LINK_R1_R3 = json_link(LINK_R1_R3_UUID, LINK_R1_R3_EPIDS) # ----- Service -------------------------------------------------------------------------------------------------------- -SERVICE_R1_R2_UUID = 'SVC:R1/EP100-R2/EP100' +SERVICE_R1_R2_UUID = 'f0432e7b-bb83-4880-9c5d-008c4925ce7d' SERVICE_R1_R2_ID = json_service_id(SERVICE_R1_R2_UUID, context_id=CONTEXT_ID) SERVICE_R1_R2_EPIDS = [ - json_endpoint_id(DEVICE_R1_ID, 'EP100', topology_id=TOPOLOGY_ID), - json_endpoint_id(DEVICE_R2_ID, 'EP100', topology_id=TOPOLOGY_ID), + json_endpoint_id(DEVICE_R1_ID, EP100, topology_id=TOPOLOGY_ID), + json_endpoint_id(DEVICE_R2_ID, EP100, topology_id=TOPOLOGY_ID), ] SERVICE_R1_R2_CONST = [ json_constraint('latency_ms', '15.2'), @@ -148,7 +148,7 @@ SERVICE_R1_R2 = json_service_l3nm_planned( config_rules=SERVICE_R1_R2_RULES) -SERVICE_R1_R3_UUID = 'SVC:R1/EP100-R3/EP100' +SERVICE_R1_R3_UUID = 'fab21cef-542a-4948-bb4a-a0468abfa925' SERVICE_R1_R3_ID = json_service_id(SERVICE_R1_R3_UUID, context_id=CONTEXT_ID) SERVICE_R1_R3_EPIDS = [ json_endpoint_id(DEVICE_R1_ID, 'EP100', topology_id=TOPOLOGY_ID), @@ -168,7 +168,7 @@ SERVICE_R1_R3 = json_service_l3nm_planned( config_rules=SERVICE_R1_R3_RULES) -SERVICE_R2_R3_UUID = 'SVC:R2/EP100-R3/EP100' +SERVICE_R2_R3_UUID = '1f2a808f-62bb-4eaa-94fb-448ed643e61a' SERVICE_R2_R3_ID = json_service_id(SERVICE_R2_R3_UUID, context_id=CONTEXT_ID) SERVICE_R2_R3_EPIDS = [ json_endpoint_id(DEVICE_R2_ID, 'EP100', topology_id=TOPOLOGY_ID), diff --git a/src/context/tests/test_unitary.py b/src/context/tests/test_unitary.py index f238e95d9..40234adcb 100644 --- a/src/context/tests/test_unitary.py +++ b/src/context/tests/test_unitary.py @@ -42,8 +42,6 @@ from context.service.rest_server.Resources import RESOURCES from requests import Session from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from context.service.database.ContextModel import ContextModel -from context.service.database.TopologyModel import TopologyModel from context.service.database.Base import Base from .Objects import ( @@ -106,7 +104,6 @@ def context_service_grpc(context_s_mb : Tuple[Database, MessageBroker]): # pylin _service.start() yield _service _service.stop() -""" @pytest.fixture(scope='session') def context_service_rest(context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name database = context_db_mb[0] @@ -118,7 +115,6 @@ def context_service_rest(context_db_mb : Tuple[Database, MessageBroker]): # pyli yield _rest_server _rest_server.shutdown() _rest_server.join() -""" @pytest.fixture(scope='session') def context_client_grpc(context_service_grpc : ContextService): # pylint: disable=redefined-outer-name _client = ContextClient() @@ -135,7 +131,7 @@ def do_rest_request(url : str): return reply.json() """ -# ----- Test gRPC methods ---------------------------------------------------------------------------------------------- +"""# ----- Test gRPC methods ---------------------------------------------------------------------------------------------- def test_grpc_context( context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name context_s_mb : Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name @@ -163,7 +159,7 @@ def test_grpc_context( assert len(response.contexts) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ - db_entries = database.get_all(ContextModel) + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) for db_entry in db_entries: LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover @@ -213,11 +209,11 @@ def test_grpc_context( assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Dump state of database after create/update the object ------------------------------------------------------ - db_entries = database.get_all(ContextModel) + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - # for db_entry in db_entries: - # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + for db_entry in db_entries: + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 1 @@ -251,14 +247,15 @@ def test_grpc_context( events_collector.stop() # ----- Dump state of database after remove the object ------------------------------------------------------------- - db_entries = database.get_all(ContextModel) + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - # for db_entry in db_entries: - # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + for db_entry in db_entries: + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 + def test_grpc_topology( context_client_grpc: ContextClient, # pylint: disable=redefined-outer-name context_s_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name @@ -294,12 +291,12 @@ def test_grpc_topology( assert len(response.topologies) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ - db_entries = database.get_all(TopologyModel) + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - # for db_entry in db_entries: - # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + for db_entry in db_entries: + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 0 + assert len(db_entries) == 1 # ----- Create the object ------------------------------------------------------------------------------------------ response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) @@ -336,12 +333,12 @@ def test_grpc_topology( # assert event.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID # ----- Dump state of database after create/update the object ------------------------------------------------------ - db_entries = database.get_all(TopologyModel) + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - # for db_entry in db_entries: - # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + for db_entry in db_entries: + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 1 + assert len(db_entries) == 2 # ----- Get when the object exists --------------------------------------------------------------------------------- response = context_client_grpc.GetTopology(TopologyId(**TOPOLOGY_ID)) @@ -383,13 +380,14 @@ def test_grpc_topology( # events_collector.stop() # ----- Dump state of database after remove the object ------------------------------------------------------------- - db_entries = database.get_all(TopologyModel) + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - # for db_entry in db_entries: - # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + for db_entry in db_entries: + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 + def test_grpc_device( context_client_grpc: ContextClient, # pylint: disable=redefined-outer-name context_s_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name @@ -439,8 +437,8 @@ def test_grpc_device( # ----- Dump state of database before create the object ------------------------------------------------------------ db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - # for db_entry in db_entries: - # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + for db_entry in db_entries: + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 2 @@ -476,8 +474,8 @@ def test_grpc_device( # ----- Dump state of database after create/update the object ------------------------------------------------------ db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - # for db_entry in db_entries: - # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + for db_entry in db_entries: + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 36 @@ -529,12 +527,12 @@ def test_grpc_device( # ----- Dump state of database after creating the object relation -------------------------------------------------- db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - # for db_entry in db_entries: - # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + for db_entry in db_entries: + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 33 + assert len(db_entries) == 36 - # ----- Remove the object ------------------------------------------------------------------------------------------ + # ----- Remove the object -------------------------------ro----------------------------------------------------------- context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R1_ID)) context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID)) context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) @@ -561,19 +559,21 @@ def test_grpc_device( # ----- Dump state of database after remove the object ------------------------------------------------------------- db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - # for db_entry in db_entries: - # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + for db_entry in db_entries: + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 - """ + def test_grpc_link( - context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name - context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name - context_database = context_db_mb[0] + context_client_grpc: ContextClient, # pylint: disable=redefined-outer-name + context_s_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name + session = context_s_mb[0] + + database = Database(session) # ----- Clean the database ----------------------------------------------------------------------------------------- - context_database.clear_all() + database.clear() # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- events_collector = EventsCollector(context_client_grpc) @@ -592,25 +592,24 @@ def test_grpc_link( response = context_client_grpc.SetDevice(Device(**DEVICE_R2)) assert response.device_uuid.uuid == DEVICE_R2_UUID + # events = events_collector.get_events(block=True, count=4) - events = events_collector.get_events(block=True, count=4) - - assert isinstance(events[0], ContextEvent) - assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE - assert events[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - - assert isinstance(events[1], TopologyEvent) - assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_CREATE - assert events[1].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert events[1].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID - - assert isinstance(events[2], DeviceEvent) - assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_CREATE - assert events[2].device_id.device_uuid.uuid == DEVICE_R1_UUID - - assert isinstance(events[3], DeviceEvent) - assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_CREATE - assert events[3].device_id.device_uuid.uuid == DEVICE_R2_UUID + # assert isinstance(events[0], ContextEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # + # assert isinstance(events[1], TopologyEvent) + # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[1].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert events[1].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + # + # assert isinstance(events[2], DeviceEvent) + # assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[2].device_id.device_uuid.uuid == DEVICE_R1_UUID + # + # assert isinstance(events[3], DeviceEvent) + # assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[3].device_id.device_uuid.uuid == DEVICE_R2_UUID # ----- Get when the object does not exist ------------------------------------------------------------------------- with pytest.raises(grpc.RpcError) as e: @@ -626,40 +625,39 @@ def test_grpc_link( assert len(response.links) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ - db_entries = context_database.dump() + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 67 + assert len(db_entries) == 44 # ----- Create the object ------------------------------------------------------------------------------------------ response = context_client_grpc.SetLink(Link(**LINK_R1_R2)) assert response.link_uuid.uuid == LINK_R1_R2_UUID # ----- Check create event ----------------------------------------------------------------------------------------- - event = events_collector.get_event(block=True) - assert isinstance(event, LinkEvent) - assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE - assert event.link_id.link_uuid.uuid == LINK_R1_R2_UUID + # event = events_collector.get_event(block=True) + # assert isinstance(event, LinkEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert event.link_id.link_uuid.uuid == LINK_R1_R2_UUID # ----- Update the object ------------------------------------------------------------------------------------------ response = context_client_grpc.SetLink(Link(**LINK_R1_R2)) assert response.link_uuid.uuid == LINK_R1_R2_UUID - # ----- Check update event ----------------------------------------------------------------------------------------- - event = events_collector.get_event(block=True) - assert isinstance(event, LinkEvent) - assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE - assert event.link_id.link_uuid.uuid == LINK_R1_R2_UUID + # event = events_collector.get_event(block=True) + # assert isinstance(event, LinkEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + # assert event.link_id.link_uuid.uuid == LINK_R1_R2_UUID # ----- Dump state of database after create/update the object ------------------------------------------------------ - db_entries = context_database.dump() + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 75 + assert len(db_entries) == 48 # ----- Get when the object exists --------------------------------------------------------------------------------- response = context_client_grpc.GetLink(LinkId(**LINK_R1_R2_ID)) @@ -674,6 +672,7 @@ def test_grpc_link( response = context_client_grpc.ListLinks(Empty()) assert len(response.links) == 1 assert response.links[0].link_id.link_uuid.uuid == LINK_R1_R2_UUID + assert len(response.links[0].link_endpoint_ids) == 2 # ----- Create object relation ------------------------------------------------------------------------------------- @@ -684,28 +683,28 @@ def test_grpc_link( assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID # ----- Check update event ----------------------------------------------------------------------------------------- - event = events_collector.get_event(block=True) - assert isinstance(event, TopologyEvent) - assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE - assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + # event = events_collector.get_event(block=True) + # assert isinstance(event, TopologyEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + # assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID # ----- Check relation was created --------------------------------------------------------------------------------- response = context_client_grpc.GetTopology(TopologyId(**TOPOLOGY_ID)) assert response.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID assert response.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID assert len(response.device_ids) == 2 - assert response.device_ids[0].device_uuid.uuid == DEVICE_R1_UUID - assert response.device_ids[1].device_uuid.uuid == DEVICE_R2_UUID + # assert response.device_ids[0].device_uuid.uuid == DEVICE_R1_UUID + # assert response.device_ids[1].device_uuid.uuid == DEVICE_R2_UUID assert len(response.link_ids) == 1 assert response.link_ids[0].link_uuid.uuid == LINK_R1_R2_UUID - db_entries = context_database.dump() + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 75 + assert len(db_entries) == 48 # ----- Remove the object ------------------------------------------------------------------------------------------ context_client_grpc.RemoveLink(LinkId(**LINK_R1_R2_ID)) @@ -715,48 +714,47 @@ def test_grpc_link( context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) # ----- Check remove event ----------------------------------------------------------------------------------------- - events = events_collector.get_events(block=True, count=5) - - assert isinstance(events[0], LinkEvent) - assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - assert events[0].link_id.link_uuid.uuid == LINK_R1_R2_UUID - - assert isinstance(events[1], DeviceEvent) - assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - assert events[1].device_id.device_uuid.uuid == DEVICE_R1_UUID - - assert isinstance(events[2], DeviceEvent) - assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - assert events[2].device_id.device_uuid.uuid == DEVICE_R2_UUID - - assert isinstance(events[3], TopologyEvent) - assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - assert events[3].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert events[3].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID - - assert isinstance(events[4], ContextEvent) - assert events[4].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - assert events[4].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # events = events_collector.get_events(block=True, count=5) + # + # assert isinstance(events[0], LinkEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[0].link_id.link_uuid.uuid == LINK_R1_R2_UUID + # + # assert isinstance(events[1], DeviceEvent) + # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[1].device_id.device_uuid.uuid == DEVICE_R1_UUID + # + # assert isinstance(events[2], DeviceEvent) + # assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[2].device_id.device_uuid.uuid == DEVICE_R2_UUID + # + # assert isinstance(events[3], TopologyEvent) + # assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[3].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert events[3].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + # + # assert isinstance(events[4], ContextEvent) + # assert events[4].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[4].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- events_collector.stop() # ----- Dump state of database after remove the object ------------------------------------------------------------- - db_entries = context_database.dump() + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 - def test_grpc_service( context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name - context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name - context_database = context_db_mb[0] - + context_s_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name + Session = context_s_mb[0] # ----- Clean the database ----------------------------------------------------------------------------------------- - context_database.clear_all() + database = Database(Session) + database.clear() # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- events_collector = EventsCollector(context_client_grpc) @@ -775,55 +773,58 @@ def test_grpc_service( response = context_client_grpc.SetDevice(Device(**DEVICE_R2)) assert response.device_uuid.uuid == DEVICE_R2_UUID - - events = events_collector.get_events(block=True, count=4) - - assert isinstance(events[0], ContextEvent) - assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE - assert events[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - - assert isinstance(events[1], TopologyEvent) - assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_CREATE - assert events[1].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - assert events[1].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID - - assert isinstance(events[2], DeviceEvent) - assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_CREATE - assert events[2].device_id.device_uuid.uuid == DEVICE_R1_UUID - - assert isinstance(events[3], DeviceEvent) - assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_CREATE - assert events[3].device_id.device_uuid.uuid == DEVICE_R2_UUID + # events = events_collector.get_events(block=True, count=4) + # + # assert isinstance(events[0], ContextEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # + # assert isinstance(events[1], TopologyEvent) + # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[1].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert events[1].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + # + # assert isinstance(events[2], DeviceEvent) + # assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[2].device_id.device_uuid.uuid == DEVICE_R1_UUID + # + # assert isinstance(events[3], DeviceEvent) + # assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[3].device_id.device_uuid.uuid == DEVICE_R2_UUID + LOGGER.info('----------------') # ----- Get when the object does not exist ------------------------------------------------------------------------- with pytest.raises(grpc.RpcError) as e: context_client_grpc.GetService(ServiceId(**SERVICE_R1_R2_ID)) assert e.value.code() == grpc.StatusCode.NOT_FOUND - assert e.value.details() == 'Service({:s}/{:s}) not found'.format(DEFAULT_CONTEXT_UUID, SERVICE_R1_R2_UUID) + assert e.value.details() == 'Service({:s}) not found'.format(SERVICE_R1_R2_UUID) + LOGGER.info('----------------') # ----- List when the object does not exist ------------------------------------------------------------------------ response = context_client_grpc.ListServiceIds(ContextId(**CONTEXT_ID)) assert len(response.service_ids) == 0 + LOGGER.info('----------------') response = context_client_grpc.ListServices(ContextId(**CONTEXT_ID)) assert len(response.services) == 0 + LOGGER.info('----------------') # ----- Dump state of database before create the object ------------------------------------------------------------ - db_entries = context_database.dump() + db_entries = database.dump_all() LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 67 + assert len(db_entries) == 44 # ----- Create the object ------------------------------------------------------------------------------------------ with pytest.raises(grpc.RpcError) as e: WRONG_SERVICE = copy.deepcopy(SERVICE_R1_R2) WRONG_SERVICE['service_endpoint_ids'][0]\ - ['topology_id']['context_id']['context_uuid']['uuid'] = 'wrong-context-uuid' + ['topology_id']['context_id']['context_uuid']['uuid'] = 'ca1ea172-728f-441d-972c-feeae8c9bffc' context_client_grpc.SetService(Service(**WRONG_SERVICE)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT - msg = 'request.service_endpoint_ids[0].topology_id.context_id.context_uuid.uuid(wrong-context-uuid) is invalid; '\ + msg = 'request.service_endpoint_ids[0].topology_id.context_id.context_uuid.uuid(ca1ea172-728f-441d-972c-feeae8c9bffc) is invalid; '\ 'should be == request.service_id.context_id.context_uuid.uuid({:s})'.format(DEFAULT_CONTEXT_UUID) assert e.value.details() == msg @@ -935,15 +936,18 @@ def test_grpc_service( LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 +""" def test_grpc_connection( context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name - context_database = context_db_mb[0] + Session = context_s_mb[0] + + database = Database(Session) # ----- Clean the database ----------------------------------------------------------------------------------------- - context_database.clear_all() + database.clear() # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- events_collector = EventsCollector(context_client_grpc) @@ -1188,6 +1192,7 @@ def test_grpc_connection( LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 +""" # ----- Test REST API methods ------------------------------------------------------------------------------------------ -- GitLab From 05a7465e3161f819d08d78cce34b937d94305294 Mon Sep 17 00:00:00 2001 From: longllu Date: Tue, 13 Dec 2022 16:36:37 +0000 Subject: [PATCH 078/353] WebUI : - Adding the "Add configuration" form. - Adding the "Update device" form (not working). --- src/webui/service/device/forms.py | 18 ++++- src/webui/service/device/routes.py | 71 +++++++++++++++++++ .../service/templates/device/addconfig.html | 69 ++++++++++++++++++ .../service/templates/device/detail.html | 22 +++++- .../service/templates/device/update.html | 34 +++++++++ .../templates/device/updateconfig.html | 18 +++++ 6 files changed, 229 insertions(+), 3 deletions(-) create mode 100644 src/webui/service/templates/device/addconfig.html create mode 100644 src/webui/service/templates/device/update.html create mode 100644 src/webui/service/templates/device/updateconfig.html diff --git a/src/webui/service/device/forms.py b/src/webui/service/device/forms.py index 3d728ade1..3aae96cc0 100644 --- a/src/webui/service/device/forms.py +++ b/src/webui/service/device/forms.py @@ -16,7 +16,7 @@ from flask_wtf import FlaskForm from wtforms import StringField, SelectField, TextAreaField, SubmitField, BooleanField, Form from wtforms.validators import DataRequired, Length, NumberRange, Regexp, ValidationError -from common.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum +from common.proto.context_pb2 import DeviceOperationalStatusEnum from webui.utils.form_validators import key_value_validator class AddDeviceForm(FlaskForm): @@ -41,4 +41,18 @@ class AddDeviceForm(FlaskForm): def validate_operational_status(form, field): if field.data not in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_number: - raise ValidationError('The operational status value selected is incorrect!') \ No newline at end of file + raise ValidationError('The operational status value selected is incorrect!') + +class ConfigForm(FlaskForm): + device_key_config = StringField('Key configuration') + device_value_config = StringField('Value configuration') + submit = SubmitField('Add') + + +class UpdateDeviceForm(FlaskForm): + config_operational_status = SelectField('Operational Status', + choices=[(-1, 'Select...'), (0, 'Undefined'), (1, 'Disabled'), (2, 'Enabled')], + coerce=int, + validators=[NumberRange(min=0)]) + + submit = SubmitField('Update') diff --git a/src/webui/service/device/routes.py b/src/webui/service/device/routes.py index 220a3a33c..105fe3b98 100644 --- a/src/webui/service/device/routes.py +++ b/src/webui/service/device/routes.py @@ -24,6 +24,8 @@ from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from webui.service.device.forms import AddDeviceForm from common.DeviceTypes import DeviceTypeEnum +from webui.service.device.forms import ConfigForm +from webui.service.device.forms import UpdateDeviceForm device = Blueprint('device', __name__, url_prefix='/device') context_client = ContextClient() @@ -161,3 +163,72 @@ def delete(device_uuid): flash(f'Problem deleting device "{device_uuid}": {e.details()}', 'danger') current_app.logger.exception(e) return redirect(url_for('device.home')) + +@device.route('/addconfig', methods=['GET', 'POST']) +def addconfig(device_uuid): + form = ConfigForm() + request = DeviceId() + request.device_uuid.uuid = device_uuid + context_client.connect() + response = context_client.GetDevice(request) + context_client.close() + + if form.validate_on_submit(): + device = Device() + device.CopyFrom(response) + config_rule = device.device_config.config_rules.add() + config_rule.action = ConfigActionEnum.CONFIGACTION_SET + config_rule.custom.resource_key = form.device_key_config.data + config_rule.custom.resource_value = form.device_value_config.data + try: + device_client.connect() + response: DeviceId = device_client.ConfigureDevice(device) + device_client.close() + flash(f'New configuration was created with ID "{response.device_uuid.uuid}".', 'success') + return redirect(url_for('device.home')) + except Exception as e: + flash(f'Problem adding the device. {e.details()}', 'danger') + + return render_template('device/addconfig.html', form=form, submit_text='Add New Configuration') + +@device.route('updateconfig', methods=['GET', 'POST']) +def updateconfig(): + + + return render_template('device/updateconfig.html') + + +@device.route('/update', methods=['GET', 'POST']) +def update(device_uuid): + form = UpdateDeviceForm() + request = DeviceId() + request.device_uuid.uuid = device_uuid + context_client.connect() + response = context_client.GetDevice(request) + context_client.close() + + ## listing enum values + #form.operational_status.choices = [] + #for key, value in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_name.items(): + # form.operational_status.choices.append((DeviceOperationalStatusEnum.Value(key), key.replace('DEVICEOPERATIONALSTATUS_', ''))) +# + #form.operational_status.default = response.device_operational_status + print(form.errors) + if form.is_submitted(): + print("submitted") + if form.validate(): + print("valid") + print(form.errors) + #if form.validate_on_submit(): + # device = Device() + # device.CopyFrom(response) + # device.device_operational_status = form.operational_status.data + # try: + # device_client.connect() + # response: DeviceId = device_client.ConfigureDevice(device) + # device_client.close() + # flash(f'New configuration was created with ID "{response.device_uuid.uuid}".', 'success') + # return redirect(url_for('device.home')) + # except Exception as e: + # flash(f'Problem adding the device. {e.details()}', 'danger') + return render_template('device/update.html', device=response, form=form, submit_text='Update Device') diff --git a/src/webui/service/templates/device/addconfig.html b/src/webui/service/templates/device/addconfig.html new file mode 100644 index 000000000..c6a17e0a5 --- /dev/null +++ b/src/webui/service/templates/device/addconfig.html @@ -0,0 +1,69 @@ + + +{% extends 'base.html' %} + +{% block content %} +

Add New Configuration

+
+
+ {{ form.hidden_tag() }} +
+
+ {{ form.device_key_config.label(class="col-sm-2 col-form-label") }} +
+ {% if form.device_key_config.errors %} + {{ form.device_key_config(class="form-control is-invalid") }} +
+ {% for error in form.device_key_config.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.device_key_config(class="form-control") }} + {% endif %} +
+
+
+
+ {{ form.device_value_config.label(class="col-sm-2 col-form-label") }} +
+ {% if form.device_value_config.errors %} + {{ form.device_value_config(class="form-control is-invalid") }} +
+ {% for error in form.device_value_config.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.device_value_config(class="form-control") }} + {% endif %} +
+
+
+
+ + +
+
+
+{% endblock %} \ No newline at end of file diff --git a/src/webui/service/templates/device/detail.html b/src/webui/service/templates/device/detail.html index f07b9c985..e49396c4f 100644 --- a/src/webui/service/templates/device/detail.html +++ b/src/webui/service/templates/device/detail.html @@ -27,7 +27,7 @@
- + Update @@ -79,11 +79,14 @@
Configurations: + + + @@ -100,11 +103,28 @@ {% endfor %} + + {% endif %} {% endfor %}
Key Value
+ + + + + + +
+ diff --git a/src/webui/service/templates/device/update.html b/src/webui/service/templates/device/update.html new file mode 100644 index 000000000..3b25da9c1 --- /dev/null +++ b/src/webui/service/templates/device/update.html @@ -0,0 +1,34 @@ +{% extends 'base.html' %} + +{% block content %} +

Update Device {{ device.device_id.device_uuid.uuid }}

+
+
+
+
+ {{ form.config_operational_status.label(class="col-sm-2 col-form-label") }} +
+ {% if form.config_operational_status.errors %} + {{ form.config_operational_status(class="form-control is-invalid") }} +
+ {% for error in form.config_operational_status.errors %} + {{ error }} + {% endfor %} +
+ {% else %} + {{ form.config_operational_status(class="form-select") }} + {% endif %} +
+
+ + + +
+
+{% endblock %} \ No newline at end of file diff --git a/src/webui/service/templates/device/updateconfig.html b/src/webui/service/templates/device/updateconfig.html new file mode 100644 index 000000000..de217733d --- /dev/null +++ b/src/webui/service/templates/device/updateconfig.html @@ -0,0 +1,18 @@ + + + +{% extends 'base.html' %} \ No newline at end of file -- GitLab From fe2b6c2f511f4aa6db2722af0efc0afb77ea9463 Mon Sep 17 00:00:00 2001 From: mansoca Date: Wed, 14 Dec 2022 12:00:33 +0100 Subject: [PATCH 079/353] Update scalability --- .../service/database/ConnectionModel.py | 31 ++++++++++++++----- .../service/database/ConstraintModel.py | 1 + .../grpc_server/ContextServiceServicerImpl.py | 3 +- src/context/tests/test_unitary.py | 5 +-- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/context/service/database/ConnectionModel.py b/src/context/service/database/ConnectionModel.py index 4cbed43a4..1147f3859 100644 --- a/src/context/service/database/ConnectionModel.py +++ b/src/context/service/database/ConnectionModel.py @@ -19,7 +19,6 @@ from common.orm.backend.Tools import key_to_str from common.orm.fields.ForeignKeyField import ForeignKeyField from common.orm.fields.IntegerField import IntegerField from common.orm.fields.PrimaryKeyField import PrimaryKeyField -from common.orm.fields.StringField import StringField from common.orm.model.Model import Model from common.orm.HighLevel import get_object, get_or_create_object, get_related_objects, update_or_create_object from common.proto.context_pb2 import EndPointId @@ -27,10 +26,24 @@ from .EndPointModel import EndPointModel from .ServiceModel import ServiceModel from .Tools import remove_dict_key + +from sqlalchemy import Column, Enum, ForeignKey, Integer, CheckConstraint +from typing import Dict, List +from common.orm.HighLevel import get_related_objects +from common.proto.context_pb2 import ServiceStatusEnum, ServiceTypeEnum +from .ConfigModel import ConfigModel +from .ConstraintModel import ConstraintsModel +from .ContextModel import ContextModel +from .Tools import grpc_to_enum +from sqlalchemy.dialects.postgresql import UUID +from context.service.database.Base import Base +import enum +LOGGER = logging.getLogger(__name__) + LOGGER = logging.getLogger(__name__) class PathModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() + path_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) def delete(self) -> None: for db_path_hop_pk,_ in self.references(PathHopModel): @@ -44,10 +57,10 @@ class PathModel(Model): # pylint: disable=abstract-method return [remove_dict_key(path_hop, 'position') for path_hop in path_hops] class PathHopModel(Model): # pylint: disable=abstract-method - pk = PrimaryKeyField() - path_fk = ForeignKeyField(PathModel) - position = IntegerField(min_value=0, required=True) - endpoint_fk = ForeignKeyField(EndPointModel) + path_hop_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) + path_uuid = Column(UUID(as_uuid=False), ForeignKey("Path.path_uuid")) + position = Column(Integer, CheckConstraint('position >= 0'), nullable=False) + endpoint_uuid = Column(UUID(as_uuid=False), ForeignKey("EndPoint.endpoint_uuid")) def dump(self, include_position=True) -> Dict: # pylint: disable=arguments-differ db_endpoint : EndPointModel = EndPointModel(self.database, self.endpoint_fk) @@ -57,8 +70,10 @@ class PathHopModel(Model): # pylint: disable=abstract-method class ConnectionModel(Model): pk = PrimaryKeyField() - connection_uuid = StringField(required=True, allow_empty=False) - service_fk = ForeignKeyField(ServiceModel, required=False) + # connection_uuid = StringField(required=True, allow_empty=False) + connection_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) + # service_fk = ForeignKeyField(ServiceModel, required=False) + service_uuid = Column(UUID(as_uuid=False), ForeignKey("Service.service_uuid")) path_fk = ForeignKeyField(PathModel, required=True) def delete(self) -> None: diff --git a/src/context/service/database/ConstraintModel.py b/src/context/service/database/ConstraintModel.py index c5ed7504d..61c25289e 100644 --- a/src/context/service/database/ConstraintModel.py +++ b/src/context/service/database/ConstraintModel.py @@ -144,6 +144,7 @@ class ConstraintModel(Base): # pylint: disable=abstract-method __tablename__ = 'Constraint' # pk = PrimaryKeyField() # constraints_fk = ForeignKeyField(ConstraintsModel) + constraint_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) constraints_uuid = Column(UUID(as_uuid=False), ForeignKey("Constraints.constraints_uuid"), primary_key=True) # kind = EnumeratedField(ConstraintKindEnum) kind = Column(Enum(ConstraintKindEnum, create_constraint=False, native_enum=False)) diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py index 98c961007..62c281205 100644 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ b/src/context/service/grpc_server/ContextServiceServicerImpl.py @@ -683,6 +683,7 @@ class ContextServiceServicerImpl(ContextServiceServicer): # create specific constraint constraint_class, str_constraint_id, constraint_data, constraint_kind = parser(grpc_constraint) + str_constraint_id = str(uuid.uuid4()) LOGGER.info('str_constraint_id: {}'.format(str_constraint_id)) # str_constraint_key_hash = fast_hasher(':'.join([constraint_kind.value, str_constraint_id])) # str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':') @@ -697,7 +698,7 @@ class ContextServiceServicerImpl(ContextServiceServicer): # create generic constraint # constraint_fk_field_name = 'constraint_uuid'.format(constraint_kind.value) constraint_data = { - 'constraint_uuid': db_constraints.constraint_uuid, 'position': position, 'kind': constraint_kind + 'constraints_uuid': db_constraints.constraints_uuid, 'position': position, 'kind': constraint_kind } db_new_constraint = ConstraintModel(**constraint_data) diff --git a/src/context/tests/test_unitary.py b/src/context/tests/test_unitary.py index 40234adcb..6d70790ee 100644 --- a/src/context/tests/test_unitary.py +++ b/src/context/tests/test_unitary.py @@ -747,6 +747,7 @@ def test_grpc_link( LOGGER.info(db_entry) LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 +""" def test_grpc_service( context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name @@ -936,9 +937,10 @@ def test_grpc_service( LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 -""" +""" + def test_grpc_connection( context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name @@ -1192,7 +1194,6 @@ def test_grpc_connection( LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 -""" # ----- Test REST API methods ------------------------------------------------------------------------------------------ -- GitLab From d4b92b6b93552449655151839a70400d9f0f7337 Mon Sep 17 00:00:00 2001 From: mansoca Date: Wed, 14 Dec 2022 12:06:11 +0100 Subject: [PATCH 080/353] Cockroachdb files --- cluster-init.yaml | 20 ++++ cockroachdb-statefulset.yaml | 182 +++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 cluster-init.yaml create mode 100644 cockroachdb-statefulset.yaml diff --git a/cluster-init.yaml b/cluster-init.yaml new file mode 100644 index 000000000..6590ba127 --- /dev/null +++ b/cluster-init.yaml @@ -0,0 +1,20 @@ +# Generated file, DO NOT EDIT. Source: cloud/kubernetes/templates/cluster-init.yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: cluster-init + labels: + app: cockroachdb +spec: + template: + spec: + containers: + - name: cluster-init + image: cockroachdb/cockroach:v22.1.6 + imagePullPolicy: IfNotPresent + command: + - "/cockroach/cockroach" + - "init" + - "--insecure" + - "--host=cockroachdb-0.cockroachdb" + restartPolicy: OnFailure diff --git a/cockroachdb-statefulset.yaml b/cockroachdb-statefulset.yaml new file mode 100644 index 000000000..f308e8fce --- /dev/null +++ b/cockroachdb-statefulset.yaml @@ -0,0 +1,182 @@ +# Generated file, DO NOT EDIT. Source: cloud/kubernetes/templates/cockroachdb-statefulset.yaml +apiVersion: v1 +kind: Service +metadata: + # This service is meant to be used by clients of the database. It exposes a ClusterIP that will + # automatically load balance connections to the different database pods. + name: cockroachdb-public + labels: + app: cockroachdb +spec: + ports: + # The main port, served by gRPC, serves Postgres-flavor SQL, internode + # traffic and the cli. + - port: 26257 + targetPort: 26257 + name: grpc + # The secondary port serves the UI as well as health and debug endpoints. + - port: 8080 + targetPort: 8080 + name: http + selector: + app: cockroachdb +--- +apiVersion: v1 +kind: Service +metadata: + # This service only exists to create DNS entries for each pod in the stateful + # set such that they can resolve each other's IP addresses. It does not + # create a load-balanced ClusterIP and should not be used directly by clients + # in most circumstances. + name: cockroachdb + labels: + app: cockroachdb + annotations: + # Use this annotation in addition to the actual publishNotReadyAddresses + # field below because the annotation will stop being respected soon but the + # field is broken in some versions of Kubernetes: + # https://github.com/kubernetes/kubernetes/issues/58662 + service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" + # Enable automatic monitoring of all instances when Prometheus is running in the cluster. + prometheus.io/scrape: "true" + prometheus.io/path: "_status/vars" + prometheus.io/port: "8080" +spec: + ports: + - port: 26257 + targetPort: 26257 + name: grpc + - port: 8080 + targetPort: 8080 + name: http + # We want all pods in the StatefulSet to have their addresses published for + # the sake of the other CockroachDB pods even before they're ready, since they + # have to be able to talk to each other in order to become ready. + publishNotReadyAddresses: true + clusterIP: None + selector: + app: cockroachdb +--- +apiVersion: policy/v1beta1 +kind: PodDisruptionBudget +metadata: + name: cockroachdb-budget + labels: + app: cockroachdb +spec: + selector: + matchLabels: + app: cockroachdb + maxUnavailable: 1 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: cockroachdb +spec: + serviceName: "cockroachdb" + replicas: 3 + selector: + matchLabels: + app: cockroachdb + template: + metadata: + labels: + app: cockroachdb + spec: + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: app + operator: In + values: + - cockroachdb + topologyKey: kubernetes.io/hostname + containers: + - name: cockroachdb + image: cockroachdb/cockroach:v22.1.6 + imagePullPolicy: IfNotPresent + # TODO: Change these to appropriate values for the hardware that you're running. You can see + # the resources that can be allocated on each of your Kubernetes nodes by running: + # kubectl describe nodes + # Note that requests and limits should have identical values. + resources: + requests: + cpu: "250m" + memory: "1Gi" + limits: + cpu: "1" + memory: "1Gi" + ports: + - containerPort: 26257 + name: grpc + - containerPort: 8080 + name: http +# We recommend that you do not configure a liveness probe on a production environment, as this can impact the availability of production databases. +# livenessProbe: +# httpGet: +# path: "/health" +# port: http +# initialDelaySeconds: 30 +# periodSeconds: 5 + readinessProbe: + httpGet: + path: "/health?ready=1" + port: http + initialDelaySeconds: 10 + periodSeconds: 5 + failureThreshold: 2 + volumeMounts: + - name: datadir + mountPath: /cockroach/cockroach-data + env: + - name: COCKROACH_CHANNEL + value: kubernetes-insecure + - name: GOMAXPROCS + valueFrom: + resourceFieldRef: + resource: limits.cpu + divisor: "1" + - name: MEMORY_LIMIT_MIB + valueFrom: + resourceFieldRef: + resource: limits.memory + divisor: "1Mi" + command: + - "/bin/bash" + - "-ecx" + # The use of qualified `hostname -f` is crucial: + # Other nodes aren't able to look up the unqualified hostname. + - exec + /cockroach/cockroach + start + --logtostderr + --insecure + --advertise-host $(hostname -f) + --http-addr 0.0.0.0 + --join cockroachdb-0.cockroachdb,cockroachdb-1.cockroachdb,cockroachdb-2.cockroachdb + --cache $(expr $MEMORY_LIMIT_MIB / 4)MiB + --max-sql-memory $(expr $MEMORY_LIMIT_MIB / 4)MiB + # No pre-stop hook is required, a SIGTERM plus some time is all that's + # needed for graceful shutdown of a node. + terminationGracePeriodSeconds: 60 + volumes: + - name: datadir + persistentVolumeClaim: + claimName: datadir + podManagementPolicy: Parallel + updateStrategy: + type: RollingUpdate + volumeClaimTemplates: + - metadata: + name: datadir + spec: + accessModes: + - "ReadWriteOnce" + resources: + requests: + storage: 10Gi -- GitLab From 16ad5d96ccab70d41bd1b1860221bc18be5943d2 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 15 Dec 2022 10:09:33 +0000 Subject: [PATCH 081/353] Context component: - reviewing integration with CockroachDB - reviewing context REST API - reviewing database schema - reviewing code --- manifests/cockroachdb/README.md | 53 + .../cockroachdb/client-secure-operator.yaml | 51 + manifests/cockroachdb/cluster.yaml | 70 + manifests/cockroachdb/crds.yaml | 1385 ++++++++++++++++ .../cockroachdb/from_carlos/cluster-init.yaml | 0 .../from_carlos/cockroachdb-statefulset.yaml | 0 manifests/cockroachdb/operator.yaml | 602 +++++++ manifests/contextservice.yaml | 4 +- src/context/Config.py | 2 - src/context/requirements.in | 7 +- .../service/{grpc_server => }/Constants.py | 0 .../{grpc_server => }/ContextService.py | 10 +- .../service/ContextServiceServicerImpl.py | 1195 ++++++++++++++ src/context/service/Database.py | 2 +- src/context/service/Engine.py | 40 + src/context/service/__main__.py | 67 +- .../__init__.py => _old_code/Config.py} | 2 + .../service/{ => _old_code}/Populate.py | 0 .../{rest_server => _old_code}/Resources.py | 0 .../{rest_server => _old_code}/RestServer.py | 0 .../{grpc_server => _old_code}/__init__.py | 0 src/context/service/_old_code/__main__.py | 85 + src/context/service/_old_code/test_unitary.py | 1450 +++++++++++++++++ src/context/service/database/Base.py | 2 - src/context/service/database/ConfigModel.py | 2 +- .../service/database/ConnectionModel.py | 2 +- .../service/database/ConstraintModel.py | 2 +- src/context/service/database/ContextModel.py | 27 +- src/context/service/database/DeviceModel.py | 2 +- src/context/service/database/EndPointModel.py | 2 +- src/context/service/database/LinkModel.py | 2 +- .../service/database/RelationModels.py | 2 +- src/context/service/database/ServiceModel.py | 2 +- src/context/service/database/TopologyModel.py | 2 +- src/context/service/database/_Base.py | 22 + src/context/service/database/__init__.py | 1 + .../grpc_server/ContextServiceServicerImpl.py | 1213 -------------- src/context/tests/test_unitary.py | 132 +- 38 files changed, 5008 insertions(+), 1432 deletions(-) create mode 100644 manifests/cockroachdb/README.md create mode 100644 manifests/cockroachdb/client-secure-operator.yaml create mode 100644 manifests/cockroachdb/cluster.yaml create mode 100644 manifests/cockroachdb/crds.yaml rename cluster-init.yaml => manifests/cockroachdb/from_carlos/cluster-init.yaml (100%) rename cockroachdb-statefulset.yaml => manifests/cockroachdb/from_carlos/cockroachdb-statefulset.yaml (100%) create mode 100644 manifests/cockroachdb/operator.yaml rename src/context/service/{grpc_server => }/Constants.py (100%) rename src/context/service/{grpc_server => }/ContextService.py (86%) create mode 100644 src/context/service/ContextServiceServicerImpl.py create mode 100644 src/context/service/Engine.py rename src/context/service/{rest_server/__init__.py => _old_code/Config.py} (86%) rename src/context/service/{ => _old_code}/Populate.py (100%) rename src/context/service/{rest_server => _old_code}/Resources.py (100%) rename src/context/service/{rest_server => _old_code}/RestServer.py (100%) rename src/context/service/{grpc_server => _old_code}/__init__.py (100%) create mode 100644 src/context/service/_old_code/__main__.py create mode 100644 src/context/service/_old_code/test_unitary.py delete mode 100644 src/context/service/database/Base.py create mode 100644 src/context/service/database/_Base.py delete mode 100644 src/context/service/grpc_server/ContextServiceServicerImpl.py diff --git a/manifests/cockroachdb/README.md b/manifests/cockroachdb/README.md new file mode 100644 index 000000000..6807afbb0 --- /dev/null +++ b/manifests/cockroachdb/README.md @@ -0,0 +1,53 @@ +# Ref: https://www.cockroachlabs.com/docs/stable/configure-cockroachdb-kubernetes.html + +DEPLOY_PATH="manifests/cockroachdb" +OPERATOR_BASE_URL="https://raw.githubusercontent.com/cockroachdb/cockroach-operator/master" + +mkdir -p ${DEPLOY_PATH} + +# Apply Custom Resource Definition for the CockroachDB Operator +curl -o "${DEPLOY_PATH}/crds.yaml" "${OPERATOR_BASE_URL}/install/crds.yaml" +kubectl apply -f "${DEPLOY_PATH}/crds.yaml" + +# Deploy CockroachDB Operator +curl -o "${DEPLOY_PATH}/operator.yaml" "${OPERATOR_BASE_URL}/install/operator.yaml" +# edit "${DEPLOY_PATH}/operator.yaml" +# - add env var: WATCH_NAMESPACE='tfs-ccdb' +kubectl apply -f "${DEPLOY_PATH}/operator.yaml" + +# Deploy CockroachDB +curl -o "${DEPLOY_PATH}/cluster.yaml" "${OPERATOR_BASE_URL}/examples/example.yaml" +# edit "${DEPLOY_PATH}/cluster.yaml" +# - set version +# - set number of replicas +kubectl create namespace tfs-ccdb +kubectl apply --namespace tfs-ccdb -f "${DEPLOY_PATH}/cluster.yaml" + +# Deploy CockroachDB Client +curl -o "${DEPLOY_PATH}/client-secure-operator.yaml" "${OPERATOR_BASE_URL}/examples/client-secure-operator.yaml" +kubectl create --namespace tfs-ccdb -f "${DEPLOY_PATH}/client-secure-operator.yaml" + +# Add tfs user with admin rights +$ kubectl exec -it ccdb-client-secure --namespace tfs-ccdb -- ./cockroach sql --certs-dir=/cockroach/cockroach-certs --host=cockroachdb-public +-- CREATE USER tfs WITH PASSWORD 'tfs123'; +-- GRANT admin TO tfs; + +# Expose CockroachDB SQL port (26257) +PORT=$(kubectl --namespace cockroachdb get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}') +PATCH='{"data": {"'${PORT}'": "cockroachdb/cockroachdb-public:'${PORT}'"}}' +kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" + +PORT_MAP='{"containerPort": '${PORT}', "hostPort": '${PORT}'}' +CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' +PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' +kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" + +# Expose CockroachDB Console port (8080) +PORT=$(kubectl --namespace cockroachdb get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="http")].port}') +PATCH='{"data": {"'${PORT}'": "cockroachdb/cockroachdb-public:'${PORT}'"}}' +kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" + +PORT_MAP='{"containerPort": '${PORT}', "hostPort": '${PORT}'}' +CONTAINER='{"name": "nginx-ingress-microk8s", "ports": ['${PORT_MAP}']}' +PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' +kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" diff --git a/manifests/cockroachdb/client-secure-operator.yaml b/manifests/cockroachdb/client-secure-operator.yaml new file mode 100644 index 000000000..618d30ce6 --- /dev/null +++ b/manifests/cockroachdb/client-secure-operator.yaml @@ -0,0 +1,51 @@ +# Copyright 2022 The Cockroach 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 +# +# https://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. +# +# Generated, do not edit. Please edit this file instead: config/templates/client-secure-operator.yaml.in +# + +apiVersion: v1 +kind: Pod +metadata: + name: cockroachdb-client-secure +spec: + serviceAccountName: cockroachdb-sa + containers: + - name: cockroachdb-client-secure + image: cockroachdb/cockroach:v22.1.8 + imagePullPolicy: IfNotPresent + volumeMounts: + - name: client-certs + mountPath: /cockroach/cockroach-certs/ + command: + - sleep + - "2147483648" # 2^31 + terminationGracePeriodSeconds: 0 + volumes: + - name: client-certs + projected: + sources: + - secret: + name: cockroachdb-node + items: + - key: ca.crt + path: ca.crt + - secret: + name: cockroachdb-root + items: + - key: tls.crt + path: client.root.crt + - key: tls.key + path: client.root.key + defaultMode: 256 diff --git a/manifests/cockroachdb/cluster.yaml b/manifests/cockroachdb/cluster.yaml new file mode 100644 index 000000000..d36685109 --- /dev/null +++ b/manifests/cockroachdb/cluster.yaml @@ -0,0 +1,70 @@ +# Copyright 2022 The Cockroach 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 +# +# https://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. +# +# Generated, do not edit. Please edit this file instead: config/templates/example.yaml.in +# + +apiVersion: crdb.cockroachlabs.com/v1alpha1 +kind: CrdbCluster +metadata: + # this translates to the name of the statefulset that is created + name: cockroachdb +spec: + dataStore: + pvc: + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: "60Gi" + volumeMode: Filesystem + resources: + requests: + # This is intentionally low to make it work on local k3d clusters. + cpu: 100m + memory: 1Gi + limits: + cpu: 1 + memory: 4Gi + tlsEnabled: true +# You can set either a version of the db or a specific image name +# cockroachDBVersion: v22.1.12 + image: + name: cockroachdb/cockroach:v22.1.12 + # nodes refers to the number of crdb pods that are created + # via the statefulset + nodes: 3 + additionalLabels: + crdb: is-cool + # affinity is a new API field that is behind a feature gate that is + # disabled by default. To enable please see the operator.yaml file. + + # The affinity field will accept any podSpec affinity rule. + # affinity: + # podAntiAffinity: + # preferredDuringSchedulingIgnoredDuringExecution: + # - weight: 100 + # podAffinityTerm: + # labelSelector: + # matchExpressions: + # - key: app.kubernetes.io/instance + # operator: In + # values: + # - cockroachdb + # topologyKey: kubernetes.io/hostname + + # nodeSelectors used to match against + # nodeSelector: + # worker-pool-name: crdb-workers diff --git a/manifests/cockroachdb/crds.yaml b/manifests/cockroachdb/crds.yaml new file mode 100644 index 000000000..1b5cd89ae --- /dev/null +++ b/manifests/cockroachdb/crds.yaml @@ -0,0 +1,1385 @@ +# Copyright 2022 The Cockroach 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 +# +# https://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. +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: (unknown) + creationTimestamp: null + name: crdbclusters.crdb.cockroachlabs.com +spec: + group: crdb.cockroachlabs.com + names: + categories: + - all + - cockroachdb + kind: CrdbCluster + listKind: CrdbClusterList + plural: crdbclusters + shortNames: + - crdb + singular: crdbcluster + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: CrdbCluster is the CRD for the cockroachDB clusters API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: CrdbClusterSpec defines the desired state of a CockroachDB + Cluster that the operator maintains. + properties: + additionalAnnotations: + additionalProperties: + type: string + description: (Optional) Additional custom resource annotations that + are added to all resources. Changing `AdditionalAnnotations` field + will result in cockroachDB cluster restart. + type: object + additionalArgs: + description: '(Optional) Additional command line arguments for the + `cockroach` binary Default: ""' + items: + type: string + type: array + additionalLabels: + additionalProperties: + type: string + description: (Optional) Additional custom resource labels that are + added to all resources + type: object + affinity: + description: (Optional) If specified, the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node matches + the corresponding matchExpressions; the node(s) with the + highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects (i.e. + is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node has + pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may + not try to eventually evict the pod from its node. When + there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms + must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of + pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of + any node on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the anti-affinity expressions specified + by this field, but it may choose a node that violates one + or more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node has + pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces + the labelSelector applies to (matches against); + null or empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its + node. When there are multiple elements, the lists of nodes + corresponding to each podAffinityTerm are intersected, i.e. + all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on which a pod of the set of + pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which namespaces the + labelSelector applies to (matches against); null or + empty list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of + any node on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + automountServiceAccountToken: + description: '(Optional) AutomountServiceAccountToken determines whether + or not the stateful set pods should automount the service account + token. This is the default behavior in Kubernetes. For backward + compatibility reasons, this value defaults to `false` here. Default: + false' + type: boolean + cache: + description: '(Optional) The total size for caches (`--cache` command + line parameter) Default: "25%"' + type: string + clientTLSSecret: + description: '(Optional) The secret with a certificate and a private + key for root database user Default: ""' + type: string + cockroachDBVersion: + description: '(Optional) CockroachDBVersion sets the explicit version + of the cockroachDB image Default: ""' + type: string + dataStore: + description: Database disk storage configuration + properties: + hostPath: + description: (Optional) Directory from the host node's filesystem + properties: + path: + description: 'Path of the directory on the host. If the path + is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + pvc: + description: (Optional) Persistent volume to use + properties: + source: + description: (Optional) Existing PVC in the same namespace + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + spec: + description: (Optional) PVC to request a new persistent volume + properties: + accessModes: + description: 'AccessModes contains the desired access + modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'This field can be used to specify either: + * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) * An existing + custom resource that implements data population (Alpha) + In order to use custom resource types that implement + data population, the AnyVolumeDataSource feature gate + must be enabled. If the provisioner or an external controller + can support the specified data source, it will create + a new volume based on the contents of the specified + data source.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - name + type: object + resources: + description: 'Resources represents the minimum resources + the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount + of compute resources required. If Requests is omitted + for a container, it defaults to Limits if that is + explicitly specified, otherwise to an implementation-defined + value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + selector: + description: A label query over volumes to consider for + binding. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, + NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values + array must be non-empty. If the operator is + Exists or DoesNotExist, the values array must + be empty. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field + is "key", the operator is "In", and the values array + contains only "value". The requirements are ANDed. + type: object + type: object + storageClassName: + description: 'Name of the StorageClass required by the + claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume is + required by the claim. Value of Filesystem is implied + when not included in claim spec. + type: string + volumeName: + description: VolumeName is the binding reference to the + PersistentVolume backing this claim. + type: string + type: object + type: object + supportsAutoResize: + description: '(Optional) SupportsAutoResize marks that a PVC will + resize without restarting the entire cluster Default: false' + type: boolean + type: object + grpcPort: + description: '(Optional) The database port (`--port` CLI parameter + when starting the service) Default: 26258' + format: int32 + type: integer + httpPort: + description: '(Optional) The web UI port (`--http-port` CLI parameter + when starting the service) Default: 8080' + format: int32 + type: integer + image: + description: (Optional) Container image information + properties: + name: + description: 'Container image with supported CockroachDB version. + This defaults to the version pinned to the operator and requires + a full container and tag/sha name. For instance: cockroachdb/cockroachdb:v20.1' + type: string + pullPolicy: + description: '(Optional) PullPolicy for the image, which defaults + to IfNotPresent. Default: IfNotPresent' + type: string + pullSecret: + description: (Optional) Secret name containing the dockerconfig + to use for a registry that requires authentication. The secret + must be configured first by the user. + type: string + required: + - name + type: object + ingress: + description: (Optional) Ingress defines the Ingress configuration + used to expose the services using Ingress + properties: + sql: + description: (Optional) Ingress options for SQL connections Adding/changing + the SQL host will result in rolling update of the crdb cluster + nodes + properties: + annotations: + additionalProperties: + type: string + description: (Optional) Annotations related to ingress resource + type: object + host: + description: host is host to be used for exposing service + type: string + ingressClassName: + description: (Optional) IngressClassName to be used by ingress + resource + type: string + tls: + description: (Optional) TLS describes the TLS certificate + info + items: + description: IngressTLS describes the transport layer security + associated with an Ingress. + properties: + hosts: + description: Hosts are a list of hosts included in the + TLS certificate. The values in this list must match + the name/s used in the tlsSecret. Defaults to the + wildcard host setting for the loadbalancer controller + fulfilling this Ingress, if left unspecified. + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + description: SecretName is the name of the secret used + to terminate TLS traffic on port 443. Field is left + optional to allow TLS routing based on SNI hostname + alone. If the SNI host in a listener conflicts with + the "Host" header field used by an IngressRule, the + SNI host is used for termination and value of the + Host header is used for routing. + type: string + type: object + type: array + required: + - host + type: object + ui: + description: (Optional) Ingress options for UI (HTTP) connections + properties: + annotations: + additionalProperties: + type: string + description: (Optional) Annotations related to ingress resource + type: object + host: + description: host is host to be used for exposing service + type: string + ingressClassName: + description: (Optional) IngressClassName to be used by ingress + resource + type: string + tls: + description: (Optional) TLS describes the TLS certificate + info + items: + description: IngressTLS describes the transport layer security + associated with an Ingress. + properties: + hosts: + description: Hosts are a list of hosts included in the + TLS certificate. The values in this list must match + the name/s used in the tlsSecret. Defaults to the + wildcard host setting for the loadbalancer controller + fulfilling this Ingress, if left unspecified. + items: + type: string + type: array + x-kubernetes-list-type: atomic + secretName: + description: SecretName is the name of the secret used + to terminate TLS traffic on port 443. Field is left + optional to allow TLS routing based on SNI hostname + alone. If the SNI host in a listener conflicts with + the "Host" header field used by an IngressRule, the + SNI host is used for termination and value of the + Host header is used for routing. + type: string + type: object + type: array + required: + - host + type: object + type: object + logConfigMap: + description: '(Optional) LogConfigMap define the config map which + contains log configuration used to send the logs through the proper + channels in the cockroachdb. Logging configuration is available + for cockroach version v21.1.0 onwards. The logging configuration + is taken in format of yaml file, you can check the logging configuration + here (https://www.cockroachlabs.com/docs/stable/configure-logs.html#default-logging-configuration) + The default logging for cockroach version v20.x or less is stderr, + logging API is ignored for older versions. NOTE: The `data` field + of map must contain an entry called `logging.yaml` that contains + config options.' + type: string + maxSQLMemory: + description: '(Optional) The maximum in-memory storage capacity available + to store temporary data for SQL queries (`--max-sql-memory` parameter) + Default: "25%"' + type: string + maxUnavailable: + description: (Optional) The maximum number of pods that can be unavailable + during a rolling update. This number is set in the PodDistruptionBudget + and defaults to 1. + format: int32 + type: integer + minAvailable: + description: (Optional) The min number of pods that can be unavailable + during a rolling update. This number is set in the PodDistruptionBudget + and defaults to 1. + format: int32 + type: integer + nodeSelector: + additionalProperties: + type: string + description: (Optional) If specified, the pod's nodeSelector + type: object + nodeTLSSecret: + description: '(Optional) The secret with certificates and a private + key for the TLS endpoint on the database port. The standard naming + of files is expected (tls.key, tls.crt, ca.crt) Default: ""' + type: string + nodes: + description: Number of nodes (pods) in the cluster + format: int32 + minimum: 3 + type: integer + podEnvVariables: + description: '(Optional) PodEnvVariables is a slice of environment + variables that are added to the pods Default: (empty list)' + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded using + the previous defined environment variables in the container + and any service environment variables. If a variable cannot + be resolved, the reference in the input string will be unchanged. + The $(VAR_NAME) syntax can be escaped with a double $$, ie: + $$(VAR_NAME). Escaped references will never be expanded, regardless + of whether the variable exists or not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels['''']`, `metadata.annotations['''']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + resources: + description: '(Optional) Database container resource limits. Any container + limits can be specified. Default: (not specified)' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + sqlPort: + description: '(Optional) The SQL Port number Default: 26257' + format: int32 + type: integer + tlsEnabled: + description: (Optional) TLSEnabled determines if TLS is enabled for + your CockroachDB Cluster + type: boolean + tolerations: + description: (Optional) Tolerations for scheduling pods onto some + dedicated nodes + items: + description: The pod this Toleration is attached to tolerates any + taint that matches the triple using the matching + operator . + properties: + effect: + description: Effect indicates the taint effect to match. Empty + means match all taint effects. When specified, allowed values + are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match all + values and all keys. + type: string + operator: + description: Operator represents a key's relationship to the + value. Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod + can tolerate all taints of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of time + the toleration (which must be of effect NoExecute, otherwise + this field is ignored) tolerates the taint. By default, it + is not set, which means tolerate the taint forever (do not + evict). Zero and negative values will be treated as 0 (evict + immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + topologySpreadConstraints: + description: (Optional) If specified, the pod's topology spread constraints + items: + description: TopologySpreadConstraint specifies how to spread matching + pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching pods. Pods + that match this label selector are counted to determine the + number of pods in their corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector + that contains values, a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are In, NotIn, + Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. + If the operator is In or NotIn, the values array + must be non-empty. If the operator is Exists or + DoesNotExist, the values array must be empty. This + array is replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. + A single {key,value} in the matchLabels map is equivalent + to an element of matchExpressions, whose key field is + "key", the operator is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + maxSkew: + description: 'MaxSkew describes the degree to which pods may + be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, + it is the maximum permitted difference between the number + of matching pods in the target topology and the global minimum. + For example, in a 3-zone cluster, MaxSkew is set to 1, and + pods with the same labelSelector spread as 1/1/0: | zone1 + | zone2 | zone3 | | P | P | | - if MaxSkew is + 1, incoming pod can only be scheduled to zone3 to become 1/1/1; + scheduling it onto zone1(zone2) would make the ActualSkew(2-0) + on zone1(zone2) violate MaxSkew(1). - if MaxSkew is 2, incoming + pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, + it is used to give higher precedence to topologies that satisfy + it. It''s a required field. Default value is 1 and 0 is not + allowed.' + format: int32 + type: integer + topologyKey: + description: TopologyKey is the key of node labels. Nodes that + have a label with this key and identical values are considered + to be in the same topology. We consider each + as a "bucket", and try to put balanced number of pods into + each bucket. It's a required field. + type: string + whenUnsatisfiable: + description: 'WhenUnsatisfiable indicates how to deal with a + pod if it doesn''t satisfy the spread constraint. - DoNotSchedule + (default) tells the scheduler not to schedule it. - ScheduleAnyway + tells the scheduler to schedule the pod in any location, but + giving higher precedence to topologies that would help reduce + the skew. A constraint is considered "Unsatisfiable" for + an incoming pod if and only if every possible node assigment + for that pod would violate "MaxSkew" on some topology. For + example, in a 3-zone cluster, MaxSkew is set to 1, and pods + with the same labelSelector spread as 3/1/1: | zone1 | zone2 + | zone3 | | P P P | P | P | If WhenUnsatisfiable is + set to DoNotSchedule, incoming pod can only be scheduled to + zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on + zone2(zone3) satisfies MaxSkew(1). In other words, the cluster + can still be imbalanced, but scheduler won''t make it *more* + imbalanced. It''s a required field.' + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + required: + - dataStore + - nodes + type: object + status: + description: CrdbClusterStatus defines the observed state of Cluster + properties: + clusterStatus: + description: OperatorStatus represent the status of the operator(Failed, + Starting, Running or Other) + type: string + conditions: + description: List of conditions representing the current status of + the cluster resource. + items: + description: ClusterCondition represents cluster status as it is + perceived by the operator + properties: + lastTransitionTime: + description: The time when the condition was updated + format: date-time + type: string + status: + description: 'Condition status: True, False or Unknown' + type: string + type: + description: Type/Name of the condition + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + crdbcontainerimage: + description: CrdbContainerImage is the container that will be installed + type: string + operatorActions: + items: + description: ClusterAction represents cluster status as it is perceived + by the operator + properties: + lastTransitionTime: + description: The time when the condition was updated + format: date-time + type: string + message: + description: (Optional) Message related to the status of the + action + type: string + status: + description: 'Action status: Failed, Finished or Unknown' + type: string + type: + description: Type/Name of the action + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + sqlHost: + description: SQLHost is the host to be used with SQL ingress + type: string + version: + description: Database service version. Not populated and is just a + placeholder currently. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/cluster-init.yaml b/manifests/cockroachdb/from_carlos/cluster-init.yaml similarity index 100% rename from cluster-init.yaml rename to manifests/cockroachdb/from_carlos/cluster-init.yaml diff --git a/cockroachdb-statefulset.yaml b/manifests/cockroachdb/from_carlos/cockroachdb-statefulset.yaml similarity index 100% rename from cockroachdb-statefulset.yaml rename to manifests/cockroachdb/from_carlos/cockroachdb-statefulset.yaml diff --git a/manifests/cockroachdb/operator.yaml b/manifests/cockroachdb/operator.yaml new file mode 100644 index 000000000..2db3c37f8 --- /dev/null +++ b/manifests/cockroachdb/operator.yaml @@ -0,0 +1,602 @@ +# Copyright 2022 The Cockroach 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 +# +# https://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. +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: cockroach-operator + name: cockroach-operator-system +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app: cockroach-operator + name: cockroach-operator-sa + namespace: cockroach-operator-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: cockroach-operator-role +rules: +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + verbs: + - get + - patch + - update +- apiGroups: + - admissionregistration.k8s.io + resources: + - validatingwebhookconfigurations + verbs: + - get + - patch + - update +- apiGroups: + - apps + resources: + - statefulsets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - statefulsets/finalizers + verbs: + - get + - list + - watch +- apiGroups: + - apps + resources: + - statefulsets/scale + verbs: + - get + - update + - watch +- apiGroups: + - apps + resources: + - statefulsets/status + verbs: + - get + - patch + - update +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - batch + resources: + - jobs/finalizers + verbs: + - get + - list + - watch +- apiGroups: + - batch + resources: + - jobs/status + verbs: + - get +- apiGroups: + - certificates.k8s.io + resources: + - certificatesigningrequests + verbs: + - create + - delete + - get + - list + - patch + - watch +- apiGroups: + - certificates.k8s.io + resources: + - certificatesigningrequests/approval + verbs: + - update +- apiGroups: + - certificates.k8s.io + resources: + - certificatesigningrequests/status + verbs: + - get + - patch + - update +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - configmaps/status + verbs: + - get +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - list + - update +- apiGroups: + - "" + resources: + - pods + verbs: + - delete + - deletecollection + - get + - list +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create +- apiGroups: + - "" + resources: + - pods/log + verbs: + - get +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - create + - get + - list + - watch +- apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - services/finalizers + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - services/status + verbs: + - get + - patch + - update +- apiGroups: + - crdb.cockroachlabs.com + resources: + - crdbclusters + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - crdb.cockroachlabs.com + resources: + - crdbclusters/finalizers + verbs: + - update +- apiGroups: + - crdb.cockroachlabs.com + resources: + - crdbclusters/status + verbs: + - get + - patch + - update +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses/finalizers + verbs: + - get + - list + - watch +- apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - get +- apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - policy + resources: + - poddisruptionbudgets/finalizers + verbs: + - get + - list + - watch +- apiGroups: + - policy + resources: + - poddisruptionbudgets/status + verbs: + - get +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + verbs: + - create + - get + - list + - watch +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + verbs: + - create + - get + - list + - watch +- apiGroups: + - security.openshift.io + resources: + - securitycontextconstraints + verbs: + - use +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: cockroach-operator-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cockroach-operator-role +subjects: +- kind: ServiceAccount + name: cockroach-operator-sa + namespace: cockroach-operator-system +--- +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: cockroach-operator + name: cockroach-operator-webhook-service + namespace: cockroach-operator-system +spec: + ports: + - port: 443 + targetPort: 9443 + selector: + app: cockroach-operator +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: cockroach-operator + name: cockroach-operator-manager + namespace: cockroach-operator-system +spec: + replicas: 1 + selector: + matchLabels: + app: cockroach-operator + template: + metadata: + labels: + app: cockroach-operator + spec: + containers: + - args: + - -zap-log-level + - info + env: + - name: RELATED_IMAGE_COCKROACH_v20_1_4 + value: cockroachdb/cockroach:v20.1.4 + - name: RELATED_IMAGE_COCKROACH_v20_1_5 + value: cockroachdb/cockroach:v20.1.5 + - name: RELATED_IMAGE_COCKROACH_v20_1_8 + value: cockroachdb/cockroach:v20.1.8 + - name: RELATED_IMAGE_COCKROACH_v20_1_11 + value: cockroachdb/cockroach:v20.1.11 + - name: RELATED_IMAGE_COCKROACH_v20_1_12 + value: cockroachdb/cockroach:v20.1.12 + - name: RELATED_IMAGE_COCKROACH_v20_1_13 + value: cockroachdb/cockroach:v20.1.13 + - name: RELATED_IMAGE_COCKROACH_v20_1_15 + value: cockroachdb/cockroach:v20.1.15 + - name: RELATED_IMAGE_COCKROACH_v20_1_16 + value: cockroachdb/cockroach:v20.1.16 + - name: RELATED_IMAGE_COCKROACH_v20_1_17 + value: cockroachdb/cockroach:v20.1.17 + - name: RELATED_IMAGE_COCKROACH_v20_2_0 + value: cockroachdb/cockroach:v20.2.0 + - name: RELATED_IMAGE_COCKROACH_v20_2_1 + value: cockroachdb/cockroach:v20.2.1 + - name: RELATED_IMAGE_COCKROACH_v20_2_2 + value: cockroachdb/cockroach:v20.2.2 + - name: RELATED_IMAGE_COCKROACH_v20_2_3 + value: cockroachdb/cockroach:v20.2.3 + - name: RELATED_IMAGE_COCKROACH_v20_2_4 + value: cockroachdb/cockroach:v20.2.4 + - name: RELATED_IMAGE_COCKROACH_v20_2_5 + value: cockroachdb/cockroach:v20.2.5 + - name: RELATED_IMAGE_COCKROACH_v20_2_6 + value: cockroachdb/cockroach:v20.2.6 + - name: RELATED_IMAGE_COCKROACH_v20_2_8 + value: cockroachdb/cockroach:v20.2.8 + - name: RELATED_IMAGE_COCKROACH_v20_2_9 + value: cockroachdb/cockroach:v20.2.9 + - name: RELATED_IMAGE_COCKROACH_v20_2_10 + value: cockroachdb/cockroach:v20.2.10 + - name: RELATED_IMAGE_COCKROACH_v20_2_11 + value: cockroachdb/cockroach:v20.2.11 + - name: RELATED_IMAGE_COCKROACH_v20_2_12 + value: cockroachdb/cockroach:v20.2.12 + - name: RELATED_IMAGE_COCKROACH_v20_2_13 + value: cockroachdb/cockroach:v20.2.13 + - name: RELATED_IMAGE_COCKROACH_v20_2_14 + value: cockroachdb/cockroach:v20.2.14 + - name: RELATED_IMAGE_COCKROACH_v20_2_15 + value: cockroachdb/cockroach:v20.2.15 + - name: RELATED_IMAGE_COCKROACH_v20_2_16 + value: cockroachdb/cockroach:v20.2.16 + - name: RELATED_IMAGE_COCKROACH_v20_2_17 + value: cockroachdb/cockroach:v20.2.17 + - name: RELATED_IMAGE_COCKROACH_v20_2_18 + value: cockroachdb/cockroach:v20.2.18 + - name: RELATED_IMAGE_COCKROACH_v20_2_19 + value: cockroachdb/cockroach:v20.2.19 + - name: RELATED_IMAGE_COCKROACH_v21_1_0 + value: cockroachdb/cockroach:v21.1.0 + - name: RELATED_IMAGE_COCKROACH_v21_1_1 + value: cockroachdb/cockroach:v21.1.1 + - name: RELATED_IMAGE_COCKROACH_v21_1_2 + value: cockroachdb/cockroach:v21.1.2 + - name: RELATED_IMAGE_COCKROACH_v21_1_3 + value: cockroachdb/cockroach:v21.1.3 + - name: RELATED_IMAGE_COCKROACH_v21_1_4 + value: cockroachdb/cockroach:v21.1.4 + - name: RELATED_IMAGE_COCKROACH_v21_1_5 + value: cockroachdb/cockroach:v21.1.5 + - name: RELATED_IMAGE_COCKROACH_v21_1_6 + value: cockroachdb/cockroach:v21.1.6 + - name: RELATED_IMAGE_COCKROACH_v21_1_7 + value: cockroachdb/cockroach:v21.1.7 + - name: RELATED_IMAGE_COCKROACH_v21_1_9 + value: cockroachdb/cockroach:v21.1.9 + - name: RELATED_IMAGE_COCKROACH_v21_1_10 + value: cockroachdb/cockroach:v21.1.10 + - name: RELATED_IMAGE_COCKROACH_v21_1_11 + value: cockroachdb/cockroach:v21.1.11 + - name: RELATED_IMAGE_COCKROACH_v21_1_12 + value: cockroachdb/cockroach:v21.1.12 + - name: RELATED_IMAGE_COCKROACH_v21_1_13 + value: cockroachdb/cockroach:v21.1.13 + - name: RELATED_IMAGE_COCKROACH_v21_1_14 + value: cockroachdb/cockroach:v21.1.14 + - name: RELATED_IMAGE_COCKROACH_v21_1_15 + value: cockroachdb/cockroach:v21.1.15 + - name: RELATED_IMAGE_COCKROACH_v21_1_16 + value: cockroachdb/cockroach:v21.1.16 + - name: RELATED_IMAGE_COCKROACH_v21_1_17 + value: cockroachdb/cockroach:v21.1.17 + - name: RELATED_IMAGE_COCKROACH_v21_1_18 + value: cockroachdb/cockroach:v21.1.18 + - name: RELATED_IMAGE_COCKROACH_v21_1_19 + value: cockroachdb/cockroach:v21.1.19 + - name: RELATED_IMAGE_COCKROACH_v21_2_0 + value: cockroachdb/cockroach:v21.2.0 + - name: RELATED_IMAGE_COCKROACH_v21_2_1 + value: cockroachdb/cockroach:v21.2.1 + - name: RELATED_IMAGE_COCKROACH_v21_2_2 + value: cockroachdb/cockroach:v21.2.2 + - name: RELATED_IMAGE_COCKROACH_v21_2_3 + value: cockroachdb/cockroach:v21.2.3 + - name: RELATED_IMAGE_COCKROACH_v21_2_4 + value: cockroachdb/cockroach:v21.2.4 + - name: RELATED_IMAGE_COCKROACH_v21_2_5 + value: cockroachdb/cockroach:v21.2.5 + - name: RELATED_IMAGE_COCKROACH_v21_2_7 + value: cockroachdb/cockroach:v21.2.7 + - name: RELATED_IMAGE_COCKROACH_v21_2_8 + value: cockroachdb/cockroach:v21.2.8 + - name: RELATED_IMAGE_COCKROACH_v21_2_9 + value: cockroachdb/cockroach:v21.2.9 + - name: RELATED_IMAGE_COCKROACH_v21_2_10 + value: cockroachdb/cockroach:v21.2.10 + - name: RELATED_IMAGE_COCKROACH_v21_2_11 + value: cockroachdb/cockroach:v21.2.11 + - name: RELATED_IMAGE_COCKROACH_v21_2_12 + value: cockroachdb/cockroach:v21.2.12 + - name: RELATED_IMAGE_COCKROACH_v21_2_13 + value: cockroachdb/cockroach:v21.2.13 + - name: RELATED_IMAGE_COCKROACH_v21_2_14 + value: cockroachdb/cockroach:v21.2.14 + - name: RELATED_IMAGE_COCKROACH_v21_2_15 + value: cockroachdb/cockroach:v21.2.15 + - name: RELATED_IMAGE_COCKROACH_v21_2_16 + value: cockroachdb/cockroach:v21.2.16 + - name: RELATED_IMAGE_COCKROACH_v22_1_0 + value: cockroachdb/cockroach:v22.1.0 + - name: RELATED_IMAGE_COCKROACH_v22_1_1 + value: cockroachdb/cockroach:v22.1.1 + - name: RELATED_IMAGE_COCKROACH_v22_1_2 + value: cockroachdb/cockroach:v22.1.2 + - name: RELATED_IMAGE_COCKROACH_v22_1_3 + value: cockroachdb/cockroach:v22.1.3 + - name: RELATED_IMAGE_COCKROACH_v22_1_4 + value: cockroachdb/cockroach:v22.1.4 + - name: RELATED_IMAGE_COCKROACH_v22_1_5 + value: cockroachdb/cockroach:v22.1.5 + - name: RELATED_IMAGE_COCKROACH_v22_1_7 + value: cockroachdb/cockroach:v22.1.7 + - name: RELATED_IMAGE_COCKROACH_v22_1_8 + value: cockroachdb/cockroach:v22.1.8 + - name: OPERATOR_NAME + value: cockroachdb + - name: WATCH_NAMESPACE + value: tfs-ccdb + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: cockroachdb/cockroach-operator:v2.8.0 + imagePullPolicy: IfNotPresent + name: cockroach-operator + resources: + requests: + cpu: 10m + memory: 32Mi + serviceAccountName: cockroach-operator-sa +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + creationTimestamp: null + name: cockroach-operator-mutating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: cockroach-operator-webhook-service + namespace: cockroach-operator-system + path: /mutate-crdb-cockroachlabs-com-v1alpha1-crdbcluster + failurePolicy: Fail + name: mcrdbcluster.kb.io + rules: + - apiGroups: + - crdb.cockroachlabs.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - crdbclusters + sideEffects: None +--- +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + creationTimestamp: null + name: cockroach-operator-validating-webhook-configuration +webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: cockroach-operator-webhook-service + namespace: cockroach-operator-system + path: /validate-crdb-cockroachlabs-com-v1alpha1-crdbcluster + failurePolicy: Fail + name: vcrdbcluster.kb.io + rules: + - apiGroups: + - crdb.cockroachlabs.com + apiVersions: + - v1alpha1 + operations: + - CREATE + - UPDATE + resources: + - crdbclusters + sideEffects: None diff --git a/manifests/contextservice.yaml b/manifests/contextservice.yaml index 5c07971a3..8201aed3e 100644 --- a/manifests/contextservice.yaml +++ b/manifests/contextservice.yaml @@ -46,6 +46,8 @@ spec: - containerPort: 1010 - containerPort: 8080 env: + - name: CCDB_URL + value: "cockroachdb://tfs:tfs123@cockroachdb-public.cockroachdb.svc.cluster.local:26257/tfs?sslmode=require" - name: DB_BACKEND value: "redis" - name: MB_BACKEND @@ -54,8 +56,6 @@ spec: value: "0" - name: LOG_LEVEL value: "INFO" - - name: POPULATE_FAKE_DATA - value: "false" readinessProbe: exec: command: ["/bin/grpc_health_probe", "-addr=:1010"] diff --git a/src/context/Config.py b/src/context/Config.py index 6f5d1dc0b..70a332512 100644 --- a/src/context/Config.py +++ b/src/context/Config.py @@ -12,5 +12,3 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Autopopulate the component with fake data for testing purposes? -POPULATE_FAKE_DATA = False diff --git a/src/context/requirements.in b/src/context/requirements.in index 6e07456fc..6c68d692d 100644 --- a/src/context/requirements.in +++ b/src/context/requirements.in @@ -1,7 +1,8 @@ Flask==2.1.3 Flask-RESTful==0.3.9 +psycopg2-binary==2.9.3 redis==4.1.2 requests==2.27.1 -sqlalchemy==1.4.40 -sqlalchemy-cockroachdb -psycopg2-binary +SQLAlchemy==1.4.40 +sqlalchemy-cockroachdb==1.4.3 +SQLAlchemy-Utils==0.38.3 diff --git a/src/context/service/grpc_server/Constants.py b/src/context/service/Constants.py similarity index 100% rename from src/context/service/grpc_server/Constants.py rename to src/context/service/Constants.py diff --git a/src/context/service/grpc_server/ContextService.py b/src/context/service/ContextService.py similarity index 86% rename from src/context/service/grpc_server/ContextService.py rename to src/context/service/ContextService.py index efede01de..c4881ccf5 100644 --- a/src/context/service/grpc_server/ContextService.py +++ b/src/context/service/ContextService.py @@ -12,15 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +import logging, sqlalchemy from common.Constants import ServiceNameEnum from common.Settings import get_service_port_grpc from common.message_broker.MessageBroker import MessageBroker from common.proto.context_pb2_grpc import add_ContextServiceServicer_to_server from common.proto.context_policy_pb2_grpc import add_ContextPolicyServiceServicer_to_server from common.tools.service.GenericGrpcService import GenericGrpcService -from sqlalchemy.orm import Session -import logging - from .ContextServiceServicerImpl import ContextServiceServicerImpl # Custom gRPC settings @@ -28,10 +26,12 @@ GRPC_MAX_WORKERS = 200 # multiple clients might keep connections alive for Get*E LOGGER = logging.getLogger(__name__) class ContextService(GenericGrpcService): - def __init__(self, session : Session, messagebroker : MessageBroker, cls_name: str = __name__) -> None: + def __init__( + self, db_engine : sqlalchemy.engine.Engine, messagebroker : MessageBroker, cls_name: str = __name__ + ) -> None: port = get_service_port_grpc(ServiceNameEnum.CONTEXT) super().__init__(port, max_workers=GRPC_MAX_WORKERS, cls_name=cls_name) - self.context_servicer = ContextServiceServicerImpl(session, messagebroker) + self.context_servicer = ContextServiceServicerImpl(db_engine, messagebroker) def install_servicers(self): add_ContextServiceServicer_to_server(self.context_servicer, self.server) diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py new file mode 100644 index 000000000..b5725f007 --- /dev/null +++ b/src/context/service/ContextServiceServicerImpl.py @@ -0,0 +1,1195 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 grpc, json, logging, operator, sqlalchemy, threading, uuid +from sqlalchemy.orm import Session, contains_eager, selectinload, sessionmaker +from sqlalchemy.dialects.postgresql import UUID, insert +from sqlalchemy_cockroachdb import run_transaction +from typing import Dict, Iterator, List, Optional, Set, Tuple, Union +from common.message_broker.MessageBroker import MessageBroker +from common.orm.backend.Tools import key_to_str +from common.proto.context_pb2 import ( + Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, + Context, ContextEvent, ContextId, ContextIdList, ContextList, + Device, DeviceEvent, DeviceId, DeviceIdList, DeviceList, + Empty, EventTypeEnum, + Link, LinkEvent, LinkId, LinkIdList, LinkList, + Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList, + Slice, SliceEvent, SliceId, SliceIdList, SliceList, + Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList, + ConfigActionEnum, Constraint) +from common.proto.policy_pb2 import PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule +from common.proto.context_pb2_grpc import ContextServiceServicer +from common.proto.context_policy_pb2_grpc import ContextPolicyServiceServicer +from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method +from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException, NotFoundException +from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string +from context.service.Database import Database +from context.service.database.ConfigModel import ( + ConfigModel, ORM_ConfigActionEnum, ConfigRuleModel, grpc_config_rules_to_raw, update_config) +from context.service.database.ConnectionModel import ConnectionModel, set_path +from context.service.database.ConstraintModel import ( + ConstraintModel, ConstraintsModel, Union_ConstraintModel, CONSTRAINT_PARSERS, set_constraints) +from context.service.database.ContextModel import ContextModel +from context.service.database.DeviceModel import ( + DeviceModel, grpc_to_enum__device_operational_status, set_drivers, grpc_to_enum__device_driver, DriverModel) +from context.service.database.EndPointModel import EndPointModel, KpiSampleTypeModel, set_kpi_sample_types +from context.service.database.Events import notify_event +from context.service.database.KpiSampleType import grpc_to_enum__kpi_sample_type +from context.service.database.LinkModel import LinkModel +from context.service.database.PolicyRuleModel import PolicyRuleModel +from context.service.database.RelationModels import ( + ConnectionSubServiceModel, LinkEndPointModel, ServiceEndPointModel, SliceEndPointModel, SliceServiceModel, + SliceSubSliceModel, TopologyDeviceModel, TopologyLinkModel) +from context.service.database.ServiceModel import ( + ServiceModel, grpc_to_enum__service_status, grpc_to_enum__service_type) +from context.service.database.SliceModel import SliceModel, grpc_to_enum__slice_status +from context.service.database.TopologyModel import TopologyModel +from .Constants import ( + CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE, + TOPIC_TOPOLOGY) + +LOGGER = logging.getLogger(__name__) + +SERVICE_NAME = 'Context' +METHOD_NAMES = [ + 'ListConnectionIds', 'ListConnections', 'GetConnection', 'SetConnection', 'RemoveConnection', 'GetConnectionEvents', + 'ListContextIds', 'ListContexts', 'GetContext', 'SetContext', 'RemoveContext', 'GetContextEvents', + 'ListTopologyIds', 'ListTopologies', 'GetTopology', 'SetTopology', 'RemoveTopology', 'GetTopologyEvents', + 'ListDeviceIds', 'ListDevices', 'GetDevice', 'SetDevice', 'RemoveDevice', 'GetDeviceEvents', + 'ListLinkIds', 'ListLinks', 'GetLink', 'SetLink', 'RemoveLink', 'GetLinkEvents', + 'ListServiceIds', 'ListServices', 'GetService', 'SetService', 'RemoveService', 'GetServiceEvents', + 'ListSliceIds', 'ListSlices', 'GetSlice', 'SetSlice', 'RemoveSlice', 'GetSliceEvents', + 'ListPolicyRuleIds', 'ListPolicyRules', 'GetPolicyRule', 'SetPolicyRule', 'RemovePolicyRule', + 'UnsetService', 'UnsetSlice', +] +METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) + +class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceServicer): + def __init__(self, db_engine : sqlalchemy.engine.Engine, messagebroker : MessageBroker) -> None: + LOGGER.debug('Creating Servicer...') + self.db_engine = db_engine + #self.lock = threading.Lock() + #session = sessionmaker(bind=db_engine, expire_on_commit=False) + #self.session = session + #self.database = Database(session) + self.messagebroker = messagebroker + LOGGER.debug('Servicer Created') + + # ----- Context ---------------------------------------------------------------------------------------------------- + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def ListContextIds(self, request: Empty, context : grpc.ServicerContext) -> ContextIdList: + def callback(session : Session) -> List[Dict]: + obj_list : List[ContextModel] = session.query(ContextModel).all() + return [obj.dump_id() for obj in obj_list] + return ContextIdList(context_ids=run_transaction(sessionmaker(bind=self.db_engine), callback)) + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def ListContexts(self, request: Empty, context : grpc.ServicerContext) -> ContextList: + def callback(session : Session) -> List[Dict]: + obj_list : List[ContextModel] = session.query(ContextModel).all() + return [obj.dump() for obj in obj_list] + return ContextList(contexts=run_transaction(sessionmaker(bind=self.db_engine), callback)) + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def GetContext(self, request: ContextId, context : grpc.ServicerContext) -> Context: + context_uuid = str(uuid.uuid5(uuid.NAMESPACE_OID, request.context_uuid.uuid)) + def callback(session : Session) -> Optional[Dict]: + obj : Optional[ContextModel] = \ + session.query(ContextModel).filter_by(context_uuid=context_uuid).one_or_none() + return None if obj is None else obj.dump() + obj = run_transaction(sessionmaker(bind=self.db_engine), callback) + if obj is None: raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) + return Context(**obj) + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def SetContext(self, request: Context, context : grpc.ServicerContext) -> ContextId: + context_uuid = str(uuid.uuid5(uuid.NAMESPACE_OID, request.context_id.context_uuid.uuid)) + context_name = request.context_id.context_uuid.uuid + + for i, topology_id in enumerate(request.topology_ids): + topology_context_uuid = topology_id.context_id.context_uuid.uuid + if topology_context_uuid != context_uuid: + raise InvalidArgumentException( + 'request.topology_ids[{:d}].context_id.context_uuid.uuid'.format(i), topology_context_uuid, + ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)]) + + for i, service_id in enumerate(request.service_ids): + service_context_uuid = service_id.context_id.context_uuid.uuid + if service_context_uuid != context_uuid: + raise InvalidArgumentException( + 'request.service_ids[{:d}].context_id.context_uuid.uuid'.format(i), service_context_uuid, + ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)]) + + def callback(session : Session) -> Tuple[Optional[Dict], bool]: + obj : Optional[ContextModel] = \ + session.query(ContextModel).with_for_update().filter_by(context_uuid=context_uuid).one_or_none() + updated = obj is not None + obj = ContextModel(context_uuid=context_uuid, context_name=context_name) + session.merge(obj) + session.commit() + obj = session.get(ContextModel, {'context_uuid': context_uuid}) + return (None if obj is None else obj.dump_id()), updated + + obj_id,updated = run_transaction(sessionmaker(bind=self.db_engine), callback) + if obj_id is None: raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) + + #event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE + #notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': obj_id}) + return ContextId(**obj_id) + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def RemoveContext(self, request: ContextId, context : grpc.ServicerContext) -> Empty: + context_uuid = str(uuid.uuid5(uuid.NAMESPACE_OID, request.context_uuid.uuid)) + + def callback(session : Session) -> bool: + num_deleted = session.query(ContextModel).filter_by(context_uuid=context_uuid).delete() + return num_deleted > 0 + + deleted = run_transaction(sessionmaker(bind=self.db_engine), callback) + #if deleted: + # notify_event(self.messagebroker, TOPIC_CONTEXT, EventTypeEnum.EVENTTYPE_REMOVE, {'context_id': request}) + return Empty() + +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def GetContextEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ContextEvent]: +# for message in self.messagebroker.consume({TOPIC_CONTEXT}, consume_timeout=CONSUME_TIMEOUT): +# yield ContextEvent(**json.loads(message.content)) + + + # ----- Topology --------------------------------------------------------------------------------------------------- + +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListTopologyIds(self, request: ContextId, context : grpc.ServicerContext) -> TopologyIdList: +# context_uuid = request.context_uuid.uuid +# +# with self.session() as session: +# result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by(context_uuid=context_uuid).one_or_none() +# if not result: +# raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) +# +# db_topologies = result.topology +# return TopologyIdList(topology_ids=[db_topology.dump_id() for db_topology in db_topologies]) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListTopologies(self, request: ContextId, context : grpc.ServicerContext) -> TopologyList: +# context_uuid = request.context_uuid.uuid +# +# with self.session() as session: +# result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by( +# context_uuid=context_uuid).one_or_none() +# if not result: +# raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) +# +# db_topologies = result.topology +# return TopologyList(topologies=[db_topology.dump() for db_topology in db_topologies]) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology: +# topology_uuid = request.topology_uuid.uuid +# +# result, dump = self.database.get_object(TopologyModel, topology_uuid, True) +# with self.session() as session: +# devs = None +# links = None +# +# filt = {'topology_uuid': topology_uuid} +# topology_devices = session.query(TopologyDeviceModel).filter_by(**filt).all() +# if topology_devices: +# devs = [] +# for td in topology_devices: +# filt = {'device_uuid': td.device_uuid} +# devs.append(session.query(DeviceModel).filter_by(**filt).one()) +# +# filt = {'topology_uuid': topology_uuid} +# topology_links = session.query(TopologyLinkModel).filter_by(**filt).all() +# if topology_links: +# links = [] +# for tl in topology_links: +# filt = {'link_uuid': tl.link_uuid} +# links.append(session.query(LinkModel).filter_by(**filt).one()) +# +# return Topology(**result.dump(devs, links)) +# +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def SetTopology(self, request: Topology, context : grpc.ServicerContext) -> TopologyId: +# context_uuid = request.topology_id.context_id.context_uuid.uuid +# topology_uuid = request.topology_id.topology_uuid.uuid +# with self.session() as session: +# topology_add = TopologyModel(topology_uuid=topology_uuid, context_uuid=context_uuid) +# updated = True +# db_topology = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).one_or_none() +# if not db_topology: +# updated = False +# session.merge(topology_add) +# session.commit() +# db_topology = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).one_or_none() +# +# for device_id in request.device_ids: +# device_uuid = device_id.device_uuid.uuid +# td = TopologyDeviceModel(topology_uuid=topology_uuid, device_uuid=device_uuid) +# result: Tuple[TopologyDeviceModel, bool] = self.database.create_or_update(td) +# +# +# for link_id in request.link_ids: +# link_uuid = link_id.link_uuid.uuid +# db_link = session.query(LinkModel).filter( +# LinkModel.link_uuid == link_uuid).one_or_none() +# tl = TopologyLinkModel(topology_uuid=topology_uuid, link_uuid=link_uuid) +# result: Tuple[TopologyDeviceModel, bool] = self.database.create_or_update(tl) +# +# +# +# event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE +# dict_topology_id = db_topology.dump_id() +# notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) +# return TopologyId(**dict_topology_id) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def RemoveTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Empty: +# context_uuid = request.context_id.context_uuid.uuid +# topology_uuid = request.topology_uuid.uuid +# +# with self.session() as session: +# result = session.query(TopologyModel).filter_by(topology_uuid=topology_uuid, context_uuid=context_uuid).one_or_none() +# if not result: +# return Empty() +# dict_topology_id = result.dump_id() +# +# session.query(TopologyModel).filter_by(topology_uuid=topology_uuid, context_uuid=context_uuid).delete() +# session.commit() +# event_type = EventTypeEnum.EVENTTYPE_REMOVE +# notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) +# return Empty() +# +## @safe_and_metered_rpc_method(METRICS, LOGGER) +## def GetTopologyEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[TopologyEvent]: +## for message in self.messagebroker.consume({TOPIC_TOPOLOGY}, consume_timeout=CONSUME_TIMEOUT): +## yield TopologyEvent(**json.loads(message.content)) +# +# +# # ----- Device ----------------------------------------------------------------------------------------------------- +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListDeviceIds(self, request: Empty, context : grpc.ServicerContext) -> DeviceIdList: +# with self.session() as session: +# result = session.query(DeviceModel).all() +# return DeviceIdList(device_ids=[device.dump_id() for device in result]) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListDevices(self, request: Empty, context : grpc.ServicerContext) -> DeviceList: +# with self.session() as session: +# result = session.query(DeviceModel).all() +# return DeviceList(devices=[device.dump() for device in result]) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def GetDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Device: +# device_uuid = request.device_uuid.uuid +# with self.session() as session: +# result = session.query(DeviceModel).filter(DeviceModel.device_uuid == device_uuid).one_or_none() +# if not result: +# raise NotFoundException(DeviceModel.__name__.replace('Model', ''), device_uuid) +# +# rd = result.dump(include_config_rules=True, include_drivers=True, include_endpoints=True) +# +# rt = Device(**rd) +# +# return rt +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def SetDevice(self, request: Device, context : grpc.ServicerContext) -> DeviceId: +# with self.session() as session: +# device_uuid = request.device_id.device_uuid.uuid +# +# for i, endpoint in enumerate(request.device_endpoints): +# endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid +# if len(endpoint_device_uuid) == 0: +# endpoint_device_uuid = device_uuid +# if device_uuid != endpoint_device_uuid: +# raise InvalidArgumentException( +# 'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid, +# ['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)]) +# +# config_rules = grpc_config_rules_to_raw(request.device_config.config_rules) +# running_config_result = self.update_config(session, device_uuid, 'device', config_rules) +# db_running_config = running_config_result[0][0] +# config_uuid = db_running_config.config_uuid +# running_config_rules = update_config( +# self.database, device_uuid, 'device', request.device_config.config_rules) +# db_running_config = running_config_rules[0][0] +# +# new_obj = DeviceModel(**{ +# 'device_uuid' : device_uuid, +# 'device_type' : request.device_type, +# 'device_operational_status' : grpc_to_enum__device_operational_status(request.device_operational_status), +# 'device_config_uuid' : config_uuid, +# }) +# result: Tuple[DeviceModel, bool] = self.database.create_or_update(new_obj) +# db_device, updated = result +# +# self.set_drivers(db_device, request.device_drivers) +# +# for i, endpoint in enumerate(request.device_endpoints): +# endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid +# # endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid +# # if len(endpoint_device_uuid) == 0: +# # endpoint_device_uuid = device_uuid +# +# endpoint_attributes = { +# 'device_uuid' : db_device.device_uuid, +# 'endpoint_uuid': endpoint_uuid, +# 'endpoint_type': endpoint.endpoint_type, +# } +# +# endpoint_topology_context_uuid = endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid +# endpoint_topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid +# if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: +# # str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) +# +# db_topology, topo_dump = self.database.get_object(TopologyModel, endpoint_topology_uuid) +# +# topology_device = TopologyDeviceModel( +# topology_uuid=endpoint_topology_uuid, +# device_uuid=db_device.device_uuid) +# self.database.create_or_update(topology_device) +# +# endpoint_attributes['topology_uuid'] = db_topology.topology_uuid +# result : Tuple[EndPointModel, bool] = update_or_create_object( +# self.database, EndPointModel, str_endpoint_key, endpoint_attributes) +# db_endpoint, endpoint_updated = result # pylint: disable=unused-variable +# +# new_endpoint = EndPointModel(**endpoint_attributes) +# result: Tuple[EndPointModel, bool] = self.database.create_or_update(new_endpoint) +# db_endpoint, updated = result +# +# self.set_kpi_sample_types(db_endpoint, endpoint.kpi_sample_types) +# +# # event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE +# dict_device_id = db_device.dump_id() +# # notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id}) +# +# return DeviceId(**dict_device_id) +# +# def set_kpi_sample_types(self, db_endpoint: EndPointModel, grpc_endpoint_kpi_sample_types): +# db_endpoint_pk = db_endpoint.endpoint_uuid +# for kpi_sample_type in grpc_endpoint_kpi_sample_types: +# orm_kpi_sample_type = grpc_to_enum__kpi_sample_type(kpi_sample_type) +# # str_endpoint_kpi_sample_type_key = key_to_str([db_endpoint_pk, orm_kpi_sample_type.name]) +# data = {'endpoint_uuid': db_endpoint_pk, +# 'kpi_sample_type': orm_kpi_sample_type.name, +# 'kpi_uuid': str(uuid.uuid4())} +# db_endpoint_kpi_sample_type = KpiSampleTypeModel(**data) +# self.database.create(db_endpoint_kpi_sample_type) +# +# def set_drivers(self, db_device: DeviceModel, grpc_device_drivers): +# db_device_pk = db_device.device_uuid +# for driver in grpc_device_drivers: +# orm_driver = grpc_to_enum__device_driver(driver) +# str_device_driver_key = key_to_str([db_device_pk, orm_driver.name]) +# driver_config = { +# # "driver_uuid": str(uuid.uuid4()), +# "device_uuid": db_device_pk, +# "driver": orm_driver.name +# } +# db_device_driver = DriverModel(**driver_config) +# db_device_driver.device_fk = db_device +# db_device_driver.driver = orm_driver +# +# self.database.create_or_update(db_device_driver) +# +# def update_config( +# self, session, db_parent_pk: str, config_name: str, +# raw_config_rules: List[Tuple[ORM_ConfigActionEnum, str, str]] +# ) -> List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]]: +# +# created = False +# +# db_config = session.query(ConfigModel).filter_by(**{ConfigModel.main_pk_name(): db_parent_pk}).one_or_none() +# if not db_config: +# db_config = ConfigModel() +# setattr(db_config, ConfigModel.main_pk_name(), db_parent_pk) +# session.add(db_config) +# session.commit() +# created = True +# +# LOGGER.info('UPDATED-CONFIG: {}'.format(db_config.dump())) +# +# db_objects: List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]] = [(db_config, created)] +# +# for position, (action, resource_key, resource_value) in enumerate(raw_config_rules): +# if action == ORM_ConfigActionEnum.SET: +# result : Tuple[ConfigRuleModel, bool] = self.set_config_rule( +# db_config, position, resource_key, resource_value) +# db_config_rule, updated = result +# db_objects.append((db_config_rule, updated)) +# elif action == ORM_ConfigActionEnum.DELETE: +# self.delete_config_rule(db_config, resource_key) +# else: +# msg = 'Unsupported action({:s}) for resource_key({:s})/resource_value({:s})' +# raise AttributeError( +# msg.format(str(ConfigActionEnum.Name(action)), str(resource_key), str(resource_value))) +# +# return db_objects +# +# def set_config_rule(self, db_config: ConfigModel, position: int, resource_key: str, resource_value: str, +# ): # -> Tuple[ConfigRuleModel, bool]: +# +# from src.context.service.database.Tools import fast_hasher +# str_rule_key_hash = fast_hasher(resource_key) +# str_config_rule_key = key_to_str([db_config.config_uuid, str_rule_key_hash], separator=':') +# pk = str(uuid.uuid5(uuid.UUID('9566448d-e950-425e-b2ae-7ead656c7e47'), str_config_rule_key)) +# data = {'config_rule_uuid': pk, 'config_uuid': db_config.config_uuid, 'position': position, +# 'action': ORM_ConfigActionEnum.SET, 'key': resource_key, 'value': resource_value} +# to_add = ConfigRuleModel(**data) +# +# result, updated = self.database.create_or_update(to_add) +# return result, updated +# +# def delete_config_rule( +# self, db_config: ConfigModel, resource_key: str +# ) -> None: +# +# from src.context.service.database.Tools import fast_hasher +# str_rule_key_hash = fast_hasher(resource_key) +# str_config_rule_key = key_to_str([db_config.pk, str_rule_key_hash], separator=':') +# +# db_config_rule = self.database.get_object(ConfigRuleModel, str_config_rule_key, raise_if_not_found=False) +# +# if db_config_rule is None: +# return +# db_config_rule.delete() +# +# def delete_all_config_rules(self, db_config: ConfigModel) -> None: +# +# db_config_rule_pks = db_config.references(ConfigRuleModel) +# for pk, _ in db_config_rule_pks: ConfigRuleModel(self.database, pk).delete() +# +# """ +# for position, (action, resource_key, resource_value) in enumerate(raw_config_rules): +# if action == ORM_ConfigActionEnum.SET: +# result: Tuple[ConfigRuleModel, bool] = set_config_rule( +# database, db_config, position, resource_key, resource_value) +# db_config_rule, updated = result +# db_objects.append((db_config_rule, updated)) +# elif action == ORM_ConfigActionEnum.DELETE: +# delete_config_rule(database, db_config, resource_key) +# else: +# msg = 'Unsupported action({:s}) for resource_key({:s})/resource_value({:s})' +# raise AttributeError( +# msg.format(str(ConfigActionEnum.Name(action)), str(resource_key), str(resource_value))) +# +# return db_objects +# """ +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def RemoveDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Empty: +# device_uuid = request.device_uuid.uuid +# +# with self.session() as session: +# db_device = session.query(DeviceModel).filter_by(device_uuid=device_uuid).one_or_none() +# +# session.query(TopologyDeviceModel).filter_by(device_uuid=device_uuid).delete() +# session.query(ConfigRuleModel).filter_by(config_uuid=db_device.device_config_uuid).delete() +# session.query(ConfigModel).filter_by(config_uuid=db_device.device_config_uuid).delete() +# +# if not db_device: +# return Empty() +# dict_device_id = db_device.dump_id() +# +# session.query(DeviceModel).filter_by(device_uuid=device_uuid).delete() +# session.commit() +# event_type = EventTypeEnum.EVENTTYPE_REMOVE +# notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id}) +# return Empty() +# +## @safe_and_metered_rpc_method(METRICS, LOGGER) +## def GetDeviceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[DeviceEvent]: +## for message in self.messagebroker.consume({TOPIC_DEVICE}, consume_timeout=CONSUME_TIMEOUT): +## yield DeviceEvent(**json.loads(message.content)) +# +# +# +# +# # ----- Link ------------------------------------------------------------------------------------------------------- +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListLinkIds(self, request: Empty, context : grpc.ServicerContext) -> LinkIdList: +# with self.session() as session: +# result = session.query(LinkModel).all() +# return LinkIdList(link_ids=[db_link.dump_id() for db_link in result]) +# +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListLinks(self, request: Empty, context : grpc.ServicerContext) -> LinkList: +# with self.session() as session: +# link_list = LinkList() +# +# db_links = session.query(LinkModel).all() +# +# for db_link in db_links: +# link_uuid = db_link.link_uuid +# filt = {'link_uuid': link_uuid} +# link_endpoints = session.query(LinkEndPointModel).filter_by(**filt).all() +# if link_endpoints: +# eps = [] +# for lep in link_endpoints: +# filt = {'endpoint_uuid': lep.endpoint_uuid} +# eps.append(session.query(EndPointModel).filter_by(**filt).one()) +# link_list.links.append(Link(**db_link.dump(eps))) +# +# return link_list +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def GetLink(self, request: LinkId, context : grpc.ServicerContext) -> Link: +# link_uuid = request.link_uuid.uuid +# with self.session() as session: +# result = session.query(LinkModel).filter(LinkModel.link_uuid == link_uuid).one_or_none() +# if not result: +# raise NotFoundException(LinkModel.__name__.replace('Model', ''), link_uuid) +# +# filt = {'link_uuid': link_uuid} +# link_endpoints = session.query(LinkEndPointModel).filter_by(**filt).all() +# if link_endpoints: +# eps = [] +# for lep in link_endpoints: +# filt = {'endpoint_uuid': lep.endpoint_uuid} +# eps.append(session.query(EndPointModel).filter_by(**filt).one()) +# return Link(**result.dump(eps)) +# +# rd = result.dump() +# rt = Link(**rd) +# +# return rt +# +# +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def SetLink(self, request: Link, context : grpc.ServicerContext) -> LinkId: +# link_uuid = request.link_id.link_uuid.uuid +# +# new_link = LinkModel(**{ +# 'link_uuid': link_uuid +# }) +# result: Tuple[LinkModel, bool] = self.database.create_or_update(new_link) +# db_link, updated = result +# +# for endpoint_id in request.link_endpoint_ids: +# endpoint_uuid = endpoint_id.endpoint_uuid.uuid +# endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid +# endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid +# endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid +# +# +# db_topology = None +# if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: +# db_topology: TopologyModel = self.database.get_object(TopologyModel, endpoint_topology_uuid) +# # check device is in topology +# self.database.get_object(TopologyDeviceModel, endpoint_device_uuid) +# +# +# link_endpoint = LinkEndPointModel(link_uuid=link_uuid, endpoint_uuid=endpoint_uuid) +# result: Tuple[LinkEndPointModel, bool] = self.database.create_or_update(link_endpoint) +# +# if db_topology is not None: +# topology_link = TopologyLinkModel(topology_uuid=endpoint_topology_uuid, link_uuid=link_uuid) +# result: Tuple[TopologyLinkModel, bool] = self.database.create_or_update(topology_link) +# +# event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE +# dict_link_id = db_link.dump_id() +# notify_event(self.messagebroker, TOPIC_LINK, event_type, {'link_id': dict_link_id}) +# return LinkId(**dict_link_id) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def RemoveLink(self, request: LinkId, context : grpc.ServicerContext) -> Empty: +# with self.session() as session: +# link_uuid = request.link_uuid.uuid +# +# session.query(TopologyLinkModel).filter_by(link_uuid=link_uuid).delete() +# session.query(LinkEndPointModel).filter_by(link_uuid=link_uuid).delete() +# +# result = session.query(LinkModel).filter_by(link_uuid=link_uuid).one_or_none() +# if not result: +# return Empty() +# dict_link_id = result.dump_id() +# +# session.query(LinkModel).filter_by(link_uuid=link_uuid).delete() +# session.commit() +# event_type = EventTypeEnum.EVENTTYPE_REMOVE +# notify_event(self.messagebroker, TOPIC_LINK, event_type, {'link_id': dict_link_id}) +# return Empty() +# +## @safe_and_metered_rpc_method(METRICS, LOGGER) +## def GetLinkEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[LinkEvent]: +## for message in self.messagebroker.consume({TOPIC_LINK}, consume_timeout=CONSUME_TIMEOUT): +## yield LinkEvent(**json.loads(message.content)) +# +# +# # ----- Service ---------------------------------------------------------------------------------------------------- +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListServiceIds(self, request: ContextId, context : grpc.ServicerContext) -> ServiceIdList: +# context_uuid = request.context_uuid.uuid +# +# with self.session() as session: +# db_services = session.query(ServiceModel).filter_by(context_uuid=context_uuid).all() +# return ServiceIdList(service_ids=[db_service.dump_id() for db_service in db_services]) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListServices(self, request: ContextId, context : grpc.ServicerContext) -> ServiceList: +# context_uuid = request.context_uuid.uuid +# +# with self.session() as session: +# db_services = session.query(ServiceModel).filter_by(context_uuid=context_uuid).all() +# return ServiceList(services=[db_service.dump() for db_service in db_services]) +# +# +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def GetService(self, request: ServiceId, context : grpc.ServicerContext) -> Service: +# service_uuid = request.service_uuid.uuid +# with self.session() as session: +# result = session.query(ServiceModel).filter_by(service_uuid=service_uuid).one_or_none() +# +# if not result: +# raise NotFoundException(ServiceModel.__name__.replace('Model', ''), service_uuid) +# +# return Service(**result.dump()) +# +# def set_constraint(self, db_constraints: ConstraintsModel, grpc_constraint: Constraint, position: int +# ) -> Tuple[Union_ConstraintModel, bool]: +# with self.session() as session: +# +# grpc_constraint_kind = str(grpc_constraint.WhichOneof('constraint')) +# +# parser = CONSTRAINT_PARSERS.get(grpc_constraint_kind) +# if parser is None: +# raise NotImplementedError('Constraint of kind {:s} is not implemented: {:s}'.format( +# grpc_constraint_kind, grpc_message_to_json_string(grpc_constraint))) +# +# # create specific constraint +# constraint_class, str_constraint_id, constraint_data, constraint_kind = parser(grpc_constraint) +# str_constraint_id = str(uuid.uuid4()) +# LOGGER.info('str_constraint_id: {}'.format(str_constraint_id)) +# # str_constraint_key_hash = fast_hasher(':'.join([constraint_kind.value, str_constraint_id])) +# # str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':') +# +# # result : Tuple[Union_ConstraintModel, bool] = update_or_create_object( +# # database, constraint_class, str_constraint_key, constraint_data) +# constraint_data[constraint_class.main_pk_name()] = str_constraint_id +# db_new_constraint = constraint_class(**constraint_data) +# result: Tuple[Union_ConstraintModel, bool] = self.database.create_or_update(db_new_constraint) +# db_specific_constraint, updated = result +# +# # create generic constraint +# # constraint_fk_field_name = 'constraint_uuid'.format(constraint_kind.value) +# constraint_data = { +# 'constraints_uuid': db_constraints.constraints_uuid, 'position': position, 'kind': constraint_kind +# } +# +# db_new_constraint = ConstraintModel(**constraint_data) +# result: Tuple[Union_ConstraintModel, bool] = self.database.create_or_update(db_new_constraint) +# db_constraint, updated = result +# +# return db_constraint, updated +# +# def set_constraints(self, service_uuid: str, constraints_name : str, grpc_constraints +# ) -> List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]]: +# with self.session() as session: +# # str_constraints_key = key_to_str([db_parent_pk, constraints_name], separator=':') +# # result : Tuple[ConstraintsModel, bool] = get_or_create_object(database, ConstraintsModel, str_constraints_key) +# result = session.query(ConstraintsModel).filter_by(constraints_uuid=service_uuid).one_or_none() +# created = None +# if result: +# created = True +# session.query(ConstraintsModel).filter_by(constraints_uuid=service_uuid).one_or_none() +# db_constraints = ConstraintsModel(constraints_uuid=service_uuid) +# session.add(db_constraints) +# +# db_objects = [(db_constraints, created)] +# +# for position,grpc_constraint in enumerate(grpc_constraints): +# result : Tuple[ConstraintModel, bool] = self.set_constraint( +# db_constraints, grpc_constraint, position) +# db_constraint, updated = result +# db_objects.append((db_constraint, updated)) +# +# return db_objects +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def SetService(self, request: Service, context : grpc.ServicerContext) -> ServiceId: +# with self.lock: +# with self.session() as session: +# +# context_uuid = request.service_id.context_id.context_uuid.uuid +# # db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) +# db_context = session.query(ContextModel).filter_by(context_uuid=context_uuid).one_or_none() +# +# for i,endpoint_id in enumerate(request.service_endpoint_ids): +# endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid +# if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: +# raise InvalidArgumentException( +# 'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), +# endpoint_topology_context_uuid, +# ['should be == {:s}({:s})'.format( +# 'request.service_id.context_id.context_uuid.uuid', context_uuid)]) +# +# service_uuid = request.service_id.service_uuid.uuid +# # str_service_key = key_to_str([context_uuid, service_uuid]) +# +# constraints_result = self.set_constraints(service_uuid, 'constraints', request.service_constraints) +# db_constraints = constraints_result[0][0] +# +# config_rules = grpc_config_rules_to_raw(request.service_config.config_rules) +# running_config_result = update_config(self.database, str_service_key, 'running', config_rules) +# db_running_config = running_config_result[0][0] +# +# result : Tuple[ServiceModel, bool] = update_or_create_object(self.database, ServiceModel, str_service_key, { +# 'context_fk' : db_context, +# 'service_uuid' : service_uuid, +# 'service_type' : grpc_to_enum__service_type(request.service_type), +# 'service_constraints_fk': db_constraints, +# 'service_status' : grpc_to_enum__service_status(request.service_status.service_status), +# 'service_config_fk' : db_running_config, +# }) +# db_service, updated = result +# +# for i,endpoint_id in enumerate(request.service_endpoint_ids): +# endpoint_uuid = endpoint_id.endpoint_uuid.uuid +# endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid +# endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid +# endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid +# +# str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) +# if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: +# str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) +# str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') +# +# db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) +# +# str_service_endpoint_key = key_to_str([service_uuid, str_endpoint_key], separator='--') +# result : Tuple[ServiceEndPointModel, bool] = get_or_create_object( +# self.database, ServiceEndPointModel, str_service_endpoint_key, { +# 'service_fk': db_service, 'endpoint_fk': db_endpoint}) +# #db_service_endpoint, service_endpoint_created = result +# +# event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE +# dict_service_id = db_service.dump_id() +# notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id}) +# return ServiceId(**dict_service_id) +# context_uuid = request.service_id.context_id.context_uuid.uuid +# db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) +# +# for i,endpoint_id in enumerate(request.service_endpoint_ids): +# endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid +# if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: +# raise InvalidArgumentException( +# 'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), +# endpoint_topology_context_uuid, +# ['should be == {:s}({:s})'.format( +# 'request.service_id.context_id.context_uuid.uuid', context_uuid)]) +# +# service_uuid = request.service_id.service_uuid.uuid +# str_service_key = key_to_str([context_uuid, service_uuid]) +# +# constraints_result = set_constraints( +# self.database, str_service_key, 'service', request.service_constraints) +# db_constraints = constraints_result[0][0] +# +# running_config_rules = update_config( +# self.database, str_service_key, 'service', request.service_config.config_rules) +# db_running_config = running_config_rules[0][0] +# +# result : Tuple[ServiceModel, bool] = update_or_create_object(self.database, ServiceModel, str_service_key, { +# 'context_fk' : db_context, +# 'service_uuid' : service_uuid, +# 'service_type' : grpc_to_enum__service_type(request.service_type), +# 'service_constraints_fk': db_constraints, +# 'service_status' : grpc_to_enum__service_status(request.service_status.service_status), +# 'service_config_fk' : db_running_config, +# }) +# db_service, updated = result +# +# for i,endpoint_id in enumerate(request.service_endpoint_ids): +# endpoint_uuid = endpoint_id.endpoint_uuid.uuid +# endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid +# endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid +# endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid +# +# str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) +# if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: +# str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) +# str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') +# +# db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) +# +# str_service_endpoint_key = key_to_str([service_uuid, str_endpoint_key], separator='--') +# result : Tuple[ServiceEndPointModel, bool] = get_or_create_object( +# self.database, ServiceEndPointModel, str_service_endpoint_key, { +# 'service_fk': db_service, 'endpoint_fk': db_endpoint}) +# #db_service_endpoint, service_endpoint_created = result +# +# event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE +# dict_service_id = db_service.dump_id() +# notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id}) +# return ServiceId(**dict_service_id) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def RemoveService(self, request: ServiceId, context : grpc.ServicerContext) -> Empty: +# with self.lock: +# context_uuid = request.context_id.context_uuid.uuid +# service_uuid = request.service_uuid.uuid +# db_service = ServiceModel(self.database, key_to_str([context_uuid, service_uuid]), auto_load=False) +# found = db_service.load() +# if not found: return Empty() +# +# dict_service_id = db_service.dump_id() +# db_service.delete() +# +# event_type = EventTypeEnum.EVENTTYPE_REMOVE +# notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id}) +# return Empty() +# +## @safe_and_metered_rpc_method(METRICS, LOGGER) +## def GetServiceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ServiceEvent]: +## for message in self.messagebroker.consume({TOPIC_SERVICE}, consume_timeout=CONSUME_TIMEOUT): +## yield ServiceEvent(**json.loads(message.content)) +# +# +# # ----- Slice ---------------------------------------------------------------------------------------------------- +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListSliceIds(self, request: ContextId, context : grpc.ServicerContext) -> SliceIdList: +# with self.lock: +# db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid) +# db_slices : Set[SliceModel] = get_related_objects(db_context, SliceModel) +# db_slices = sorted(db_slices, key=operator.attrgetter('pk')) +# return SliceIdList(slice_ids=[db_slice.dump_id() for db_slice in db_slices]) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListSlices(self, request: ContextId, context : grpc.ServicerContext) -> SliceList: +# with self.lock: +# db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid) +# db_slices : Set[SliceModel] = get_related_objects(db_context, SliceModel) +# db_slices = sorted(db_slices, key=operator.attrgetter('pk')) +# return SliceList(slices=[db_slice.dump() for db_slice in db_slices]) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def GetSlice(self, request: SliceId, context : grpc.ServicerContext) -> Slice: +# with self.lock: +# str_key = key_to_str([request.context_id.context_uuid.uuid, request.slice_uuid.uuid]) +# db_slice : SliceModel = get_object(self.database, SliceModel, str_key) +# return Slice(**db_slice.dump( +# include_endpoint_ids=True, include_constraints=True, include_config_rules=True, +# include_service_ids=True, include_subslice_ids=True)) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def SetSlice(self, request: Slice, context : grpc.ServicerContext) -> SliceId: +# with self.lock: +# context_uuid = request.slice_id.context_id.context_uuid.uuid +# db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) +# +# for i,endpoint_id in enumerate(request.slice_endpoint_ids): +# endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid +# if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: +# raise InvalidArgumentException( +# 'request.slice_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), +# endpoint_topology_context_uuid, +# ['should be == {:s}({:s})'.format( +# 'request.slice_id.context_id.context_uuid.uuid', context_uuid)]) +# +# slice_uuid = request.slice_id.slice_uuid.uuid +# str_slice_key = key_to_str([context_uuid, slice_uuid]) +# +# constraints_result = set_constraints( +# self.database, str_slice_key, 'slice', request.slice_constraints) +# db_constraints = constraints_result[0][0] +# +# running_config_rules = update_config( +# self.database, str_slice_key, 'slice', request.slice_config.config_rules) +# db_running_config = running_config_rules[0][0] +# +# result : Tuple[SliceModel, bool] = update_or_create_object(self.database, SliceModel, str_slice_key, { +# 'context_fk' : db_context, +# 'slice_uuid' : slice_uuid, +# 'slice_constraints_fk': db_constraints, +# 'slice_status' : grpc_to_enum__slice_status(request.slice_status.slice_status), +# 'slice_config_fk' : db_running_config, +# 'slice_owner_uuid' : request.slice_owner.owner_uuid.uuid, +# 'slice_owner_string' : request.slice_owner.owner_string, +# }) +# db_slice, updated = result +# +# for i,endpoint_id in enumerate(request.slice_endpoint_ids): +# endpoint_uuid = endpoint_id.endpoint_uuid.uuid +# endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid +# endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid +# endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid +# +# str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) +# if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: +# str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) +# str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') +# +# db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) +# +# str_slice_endpoint_key = key_to_str([str_slice_key, str_endpoint_key], separator='--') +# result : Tuple[SliceEndPointModel, bool] = get_or_create_object( +# self.database, SliceEndPointModel, str_slice_endpoint_key, { +# 'slice_fk': db_slice, 'endpoint_fk': db_endpoint}) +# #db_slice_endpoint, slice_endpoint_created = result +# +# for i,service_id in enumerate(request.slice_service_ids): +# service_uuid = service_id.service_uuid.uuid +# service_context_uuid = service_id.context_id.context_uuid.uuid +# str_service_key = key_to_str([service_context_uuid, service_uuid]) +# db_service : ServiceModel = get_object(self.database, ServiceModel, str_service_key) +# +# str_slice_service_key = key_to_str([str_slice_key, str_service_key], separator='--') +# result : Tuple[SliceServiceModel, bool] = get_or_create_object( +# self.database, SliceServiceModel, str_slice_service_key, { +# 'slice_fk': db_slice, 'service_fk': db_service}) +# #db_slice_service, slice_service_created = result +# +# for i,subslice_id in enumerate(request.slice_subslice_ids): +# subslice_uuid = subslice_id.slice_uuid.uuid +# subslice_context_uuid = subslice_id.context_id.context_uuid.uuid +# str_subslice_key = key_to_str([subslice_context_uuid, subslice_uuid]) +# db_subslice : SliceModel = get_object(self.database, SliceModel, str_subslice_key) +# +# str_slice_subslice_key = key_to_str([str_slice_key, str_subslice_key], separator='--') +# result : Tuple[SliceSubSliceModel, bool] = get_or_create_object( +# self.database, SliceSubSliceModel, str_slice_subslice_key, { +# 'slice_fk': db_slice, 'sub_slice_fk': db_subslice}) +# #db_slice_subslice, slice_subslice_created = result +# +# event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE +# dict_slice_id = db_slice.dump_id() +# notify_event(self.messagebroker, TOPIC_SLICE, event_type, {'slice_id': dict_slice_id}) +# return SliceId(**dict_slice_id) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def UnsetSlice(self, request: Slice, context : grpc.ServicerContext) -> SliceId: +# with self.lock: +# context_uuid = request.slice_id.context_id.context_uuid.uuid +# db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) +# +# for i,endpoint_id in enumerate(request.slice_endpoint_ids): +# endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid +# if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: +# raise InvalidArgumentException( +# 'request.slice_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), +# endpoint_topology_context_uuid, +# ['should be == {:s}({:s})'.format( +# 'request.slice_id.context_id.context_uuid.uuid', context_uuid)]) +# +# slice_uuid = request.slice_id.slice_uuid.uuid +# str_slice_key = key_to_str([context_uuid, slice_uuid]) +# +# if len(request.slice_constraints) > 0: +# raise NotImplementedError('UnsetSlice: removal of constraints') +# if len(request.slice_config.config_rules) > 0: +# raise NotImplementedError('UnsetSlice: removal of config rules') +# if len(request.slice_endpoint_ids) > 0: +# raise NotImplementedError('UnsetSlice: removal of endpoints') +# +# updated = False +# +# for service_id in request.slice_service_ids: +# service_uuid = service_id.service_uuid.uuid +# service_context_uuid = service_id.context_id.context_uuid.uuid +# str_service_key = key_to_str([service_context_uuid, service_uuid]) +# str_slice_service_key = key_to_str([str_slice_key, str_service_key], separator='--') +# SliceServiceModel(self.database, str_slice_service_key).delete() +# updated = True +# +# for subslice_id in request.slice_subslice_ids: +# subslice_uuid = subslice_id.slice_uuid.uuid +# subslice_context_uuid = subslice_id.context_id.context_uuid.uuid +# str_subslice_key = key_to_str([subslice_context_uuid, subslice_uuid]) +# str_slice_subslice_key = key_to_str([str_slice_key, str_subslice_key], separator='--') +# SliceSubSliceModel(self.database, str_slice_subslice_key).delete() +# updated = True +# +# event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE +# db_slice : SliceModel = get_object(self.database, SliceModel, str_slice_key) +# dict_slice_id = db_slice.dump_id() +# notify_event(self.messagebroker, TOPIC_SLICE, event_type, {'slice_id': dict_slice_id}) +# return SliceId(**dict_slice_id) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def RemoveSlice(self, request: SliceId, context : grpc.ServicerContext) -> Empty: +# with self.lock: +# context_uuid = request.context_id.context_uuid.uuid +# slice_uuid = request.slice_uuid.uuid +# db_slice = SliceModel(self.database, key_to_str([context_uuid, slice_uuid]), auto_load=False) +# found = db_slice.load() +# if not found: return Empty() +# +# dict_slice_id = db_slice.dump_id() +# db_slice.delete() +# +# event_type = EventTypeEnum.EVENTTYPE_REMOVE +# notify_event(self.messagebroker, TOPIC_SLICE, event_type, {'slice_id': dict_slice_id}) +# return Empty() +# +## @safe_and_metered_rpc_method(METRICS, LOGGER) +## def GetSliceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[SliceEvent]: +## for message in self.messagebroker.consume({TOPIC_SLICE}, consume_timeout=CONSUME_TIMEOUT): +## yield SliceEvent(**json.loads(message.content)) +# +# +# # ----- Connection ------------------------------------------------------------------------------------------------- +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListConnectionIds(self, request: ServiceId, context : grpc.ServicerContext) -> ConnectionIdList: +# with self.session() as session: +# result = session.query(DeviceModel).all() +# return DeviceIdList(device_ids=[device.dump_id() for device in result]) +# +# with self.lock: +# str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid]) +# db_service : ServiceModel = get_object(self.database, ServiceModel, str_key) +# db_connections : Set[ConnectionModel] = get_related_objects(db_service, ConnectionModel) +# db_connections = sorted(db_connections, key=operator.attrgetter('pk')) +# return ConnectionIdList(connection_ids=[db_connection.dump_id() for db_connection in db_connections]) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListConnections(self, request: ContextId, context : grpc.ServicerContext) -> ServiceList: +# with self.lock: +# str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid]) +# db_service : ServiceModel = get_object(self.database, ServiceModel, str_key) +# db_connections : Set[ConnectionModel] = get_related_objects(db_service, ConnectionModel) +# db_connections = sorted(db_connections, key=operator.attrgetter('pk')) +# return ConnectionList(connections=[db_connection.dump() for db_connection in db_connections]) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def GetConnection(self, request: ConnectionId, context : grpc.ServicerContext) -> Connection: +# with self.lock: +# db_connection : ConnectionModel = get_object(self.database, ConnectionModel, request.connection_uuid.uuid) +# return Connection(**db_connection.dump(include_path=True, include_sub_service_ids=True)) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def SetConnection(self, request: Connection, context : grpc.ServicerContext) -> ConnectionId: +# with self.lock: +# connection_uuid = request.connection_id.connection_uuid.uuid +# +# connection_attributes = {'connection_uuid': connection_uuid} +# +# service_context_uuid = request.service_id.context_id.context_uuid.uuid +# service_uuid = request.service_id.service_uuid.uuid +# if len(service_context_uuid) > 0 and len(service_uuid) > 0: +# str_service_key = key_to_str([service_context_uuid, service_uuid]) +# db_service : ServiceModel = get_object(self.database, ServiceModel, str_service_key) +# connection_attributes['service_fk'] = db_service +# +# path_hops_result = set_path(self.database, connection_uuid, request.path_hops_endpoint_ids, path_name = '') +# db_path = path_hops_result[0] +# connection_attributes['path_fk'] = db_path +# +# result : Tuple[ConnectionModel, bool] = update_or_create_object( +# self.database, ConnectionModel, connection_uuid, connection_attributes) +# db_connection, updated = result +# +# for sub_service_id in request.sub_service_ids: +# sub_service_uuid = sub_service_id.service_uuid.uuid +# sub_service_context_uuid = sub_service_id.context_id.context_uuid.uuid +# str_sub_service_key = key_to_str([sub_service_context_uuid, sub_service_uuid]) +# db_service : ServiceModel = get_object(self.database, ServiceModel, str_sub_service_key) +# +# str_connection_sub_service_key = key_to_str([connection_uuid, str_sub_service_key], separator='--') +# result : Tuple[ConnectionSubServiceModel, bool] = get_or_create_object( +# self.database, ConnectionSubServiceModel, str_connection_sub_service_key, { +# 'connection_fk': db_connection, 'sub_service_fk': db_service}) +# #db_connection_sub_service, connection_sub_service_created = result +# +# event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE +# dict_connection_id = db_connection.dump_id() +# notify_event(self.messagebroker, TOPIC_CONNECTION, event_type, {'connection_id': dict_connection_id}) +# return ConnectionId(**dict_connection_id) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def RemoveConnection(self, request: ConnectionId, context : grpc.ServicerContext) -> Empty: +# with self.lock: +# db_connection = ConnectionModel(self.database, request.connection_uuid.uuid, auto_load=False) +# found = db_connection.load() +# if not found: return Empty() +# +# dict_connection_id = db_connection.dump_id() +# db_connection.delete() +# +# event_type = EventTypeEnum.EVENTTYPE_REMOVE +# notify_event(self.messagebroker, TOPIC_CONNECTION, event_type, {'connection_id': dict_connection_id}) +# return Empty() +# +## @safe_and_metered_rpc_method(METRICS, LOGGER) +## def GetConnectionEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ConnectionEvent]: +## for message in self.messagebroker.consume({TOPIC_CONNECTION}, consume_timeout=CONSUME_TIMEOUT): +## yield ConnectionEvent(**json.loads(message.content)) +# +# +# # ----- Policy ----------------------------------------------------------------------------------------------------- +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListPolicyRuleIds(self, request: Empty, context: grpc.ServicerContext) -> PolicyRuleIdList: +# with self.lock: +# db_policy_rules: List[PolicyRuleModel] = get_all_objects(self.database, PolicyRuleModel) +# db_policy_rules = sorted(db_policy_rules, key=operator.attrgetter('pk')) +# return PolicyRuleIdList(policyRuleIdList=[db_policy_rule.dump_id() for db_policy_rule in db_policy_rules]) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def ListPolicyRules(self, request: Empty, context: grpc.ServicerContext) -> PolicyRuleList: +# with self.lock: +# db_policy_rules: List[PolicyRuleModel] = get_all_objects(self.database, PolicyRuleModel) +# db_policy_rules = sorted(db_policy_rules, key=operator.attrgetter('pk')) +# return PolicyRuleList(policyRules=[db_policy_rule.dump() for db_policy_rule in db_policy_rules]) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def GetPolicyRule(self, request: PolicyRuleId, context: grpc.ServicerContext) -> PolicyRule: +# with self.lock: +# policy_rule_uuid = request.uuid.uuid +# db_policy_rule: PolicyRuleModel = get_object(self.database, PolicyRuleModel, policy_rule_uuid) +# return PolicyRule(**db_policy_rule.dump()) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def SetPolicyRule(self, request: PolicyRule, context: grpc.ServicerContext) -> PolicyRuleId: +# with self.lock: +# policy_rule_type = request.WhichOneof('policy_rule') +# policy_rule_json = grpc_message_to_json(request) +# policy_rule_uuid = policy_rule_json[policy_rule_type]['policyRuleBasic']['policyRuleId']['uuid']['uuid'] +# result: Tuple[PolicyRuleModel, bool] = update_or_create_object( +# self.database, PolicyRuleModel, policy_rule_uuid, {'value': json.dumps(policy_rule_json)}) +# db_policy, updated = result # pylint: disable=unused-variable +# +# #event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE +# dict_policy_id = db_policy.dump_id() +# #notify_event(self.messagebroker, TOPIC_POLICY, event_type, {"policy_id": dict_policy_id}) +# return PolicyRuleId(**dict_policy_id) +# +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def RemovePolicyRule(self, request: PolicyRuleId, context: grpc.ServicerContext) -> Empty: +# with self.lock: +# policy_uuid = request.uuid.uuid +# db_policy = PolicyRuleModel(self.database, policy_uuid, auto_load=False) +# found = db_policy.load() +# if not found: return Empty() +# +# dict_policy_id = db_policy.dump_id() +# db_policy.delete() +# #event_type = EventTypeEnum.EVENTTYPE_REMOVE +# #notify_event(self.messagebroker, TOPIC_POLICY, event_type, {"policy_id": dict_policy_id}) +# return Empty() +# \ No newline at end of file diff --git a/src/context/service/Database.py b/src/context/service/Database.py index 2b699203a..8aa568239 100644 --- a/src/context/service/Database.py +++ b/src/context/service/Database.py @@ -2,7 +2,7 @@ from typing import Tuple, List from sqlalchemy import MetaData from sqlalchemy.orm import Session, joinedload -from context.service.database.Base import Base +from context.service.database._Base import Base import logging from common.orm.backend.Tools import key_to_str diff --git a/src/context/service/Engine.py b/src/context/service/Engine.py new file mode 100644 index 000000000..7944d8601 --- /dev/null +++ b/src/context/service/Engine.py @@ -0,0 +1,40 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, sqlalchemy, sqlalchemy_utils +from common.Settings import get_setting + +LOGGER = logging.getLogger(__name__) + +APP_NAME = 'tfs' + +class Engine: + def get_engine(self) -> sqlalchemy.engine.Engine: + ccdb_url = get_setting('CCDB_URL') + + try: + engine = sqlalchemy.create_engine( + ccdb_url, connect_args={'application_name': APP_NAME}, echo=False, future=True) + except: # pylint: disable=bare-except + LOGGER.exception('Failed to connect to database: {:s}'.format(ccdb_url)) + return None + + try: + if not sqlalchemy_utils.database_exists(engine.url): + sqlalchemy_utils.create_database(engine.url) + except: # pylint: disable=bare-except + LOGGER.exception('Failed to check/create to database: {:s}'.format(ccdb_url)) + return None + + return engine diff --git a/src/context/service/__main__.py b/src/context/service/__main__.py index 34942ec82..c5bbcc3f2 100644 --- a/src/context/service/__main__.py +++ b/src/context/service/__main__.py @@ -14,85 +14,52 @@ import logging, signal, sys, threading from prometheus_client import start_http_server -from common.Settings import get_log_level, get_metrics_port, get_setting +from common.Settings import get_log_level, get_metrics_port from common.message_broker.Factory import get_messagebroker_backend from common.message_broker.MessageBroker import MessageBroker -from context.Config import POPULATE_FAKE_DATA -from sqlalchemy.orm import sessionmaker, declarative_base -from context.service.database.Base import Base -from .grpc_server.ContextService import ContextService -from .rest_server.Resources import RESOURCES -from .rest_server.RestServer import RestServer -from .Populate import populate -# from models import Device, EndPoint, EndPointId, DeviceDriverEnum, DeviceOperationalStatusEnum, ConfigActionEnum, \ -# ConfigRule, KpiSampleType, Base -from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from .database import rebuild_database +from .ContextService import ContextService +from .Engine import Engine + +LOG_LEVEL = get_log_level() +logging.basicConfig(level=LOG_LEVEL, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s") +LOGGER = logging.getLogger(__name__) + +LOGGER.addHandler(logging.StreamHandler(stream=sys.stderr)) +LOGGER.setLevel(logging.WARNING) terminate = threading.Event() -LOGGER = None +LOGGER : logging.Logger = None def signal_handler(signal, frame): # pylint: disable=redefined-outer-name LOGGER.warning('Terminate signal received') terminate.set() def main(): - global LOGGER # pylint: disable=global-statement - - log_level = get_log_level() - logging.basicConfig(level=log_level, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s") - LOGGER = logging.getLogger(__name__) - + LOGGER.info('Starting...') signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) - LOGGER.info('Starting...') - # Start metrics server metrics_port = get_metrics_port() start_http_server(metrics_port) - # Get database instance - db_uri = 'cockroachdb://root@10.152.183.111:26257/defaultdb?sslmode=disable' - LOGGER.debug('Connecting to DB: {}'.format(db_uri)) - - # engine = create_engine(db_uri, echo=False) - - try: - engine = create_engine(db_uri) - except Exception as e: - LOGGER.error("Failed to connect to database.") - LOGGER.error(f"{e}") - return 1 - - Base.metadata.create_all(engine) - session = sessionmaker(bind=engine, expire_on_commit=False) + db_engine = Engine().get_engine() + rebuild_database(db_engine, drop_if_exists=False) # Get message broker instance messagebroker = MessageBroker(get_messagebroker_backend()) # Starting context service - grpc_service = ContextService(session, messagebroker) + grpc_service = ContextService(db_engine, messagebroker) grpc_service.start() - rest_server = RestServer() - for endpoint_name, resource_class, resource_url in RESOURCES: - rest_server.add_resource(resource_class, resource_url, endpoint=endpoint_name, resource_class_args=(session,)) - rest_server.start() - - populate_fake_data = get_setting('POPULATE_FAKE_DATA', default=POPULATE_FAKE_DATA) - if isinstance(populate_fake_data, str): populate_fake_data = (populate_fake_data.upper() in {'T', '1', 'TRUE'}) - if populate_fake_data: - LOGGER.info('Populating fake data...') - populate(host='127.0.0.1', port=grpc_service.bind_port) - LOGGER.info('Fake Data populated') - # Wait for Ctrl+C or termination signal while not terminate.wait(timeout=0.1): pass LOGGER.info('Terminating...') grpc_service.stop() - rest_server.shutdown() - rest_server.join() LOGGER.info('Bye') return 0 diff --git a/src/context/service/rest_server/__init__.py b/src/context/service/_old_code/Config.py similarity index 86% rename from src/context/service/rest_server/__init__.py rename to src/context/service/_old_code/Config.py index 70a332512..6f5d1dc0b 100644 --- a/src/context/service/rest_server/__init__.py +++ b/src/context/service/_old_code/Config.py @@ -12,3 +12,5 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Autopopulate the component with fake data for testing purposes? +POPULATE_FAKE_DATA = False diff --git a/src/context/service/Populate.py b/src/context/service/_old_code/Populate.py similarity index 100% rename from src/context/service/Populate.py rename to src/context/service/_old_code/Populate.py diff --git a/src/context/service/rest_server/Resources.py b/src/context/service/_old_code/Resources.py similarity index 100% rename from src/context/service/rest_server/Resources.py rename to src/context/service/_old_code/Resources.py diff --git a/src/context/service/rest_server/RestServer.py b/src/context/service/_old_code/RestServer.py similarity index 100% rename from src/context/service/rest_server/RestServer.py rename to src/context/service/_old_code/RestServer.py diff --git a/src/context/service/grpc_server/__init__.py b/src/context/service/_old_code/__init__.py similarity index 100% rename from src/context/service/grpc_server/__init__.py rename to src/context/service/_old_code/__init__.py diff --git a/src/context/service/_old_code/__main__.py b/src/context/service/_old_code/__main__.py new file mode 100644 index 000000000..69d3f5cbe --- /dev/null +++ b/src/context/service/_old_code/__main__.py @@ -0,0 +1,85 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, signal, sys, threading +from prometheus_client import start_http_server +from common.Settings import get_log_level, get_metrics_port, get_setting +from common.orm.Database import Database +from common.orm.Factory import get_database_backend +from common.message_broker.Factory import get_messagebroker_backend +from common.message_broker.MessageBroker import MessageBroker +from context.service.grpc_server.ContextService import ContextService +from .Config import POPULATE_FAKE_DATA +from .Populate import populate +from .Resources import RESOURCES +from .RestServer import RestServer + +terminate = threading.Event() +LOGGER = None + +def signal_handler(signal, frame): # pylint: disable=redefined-outer-name + LOGGER.warning('Terminate signal received') + terminate.set() + +def main(): + global LOGGER # pylint: disable=global-statement + + log_level = get_log_level() + logging.basicConfig(level=log_level, format="[%(asctime)s] %(levelname)s:%(name)s:%(message)s") + LOGGER = logging.getLogger(__name__) + + signal.signal(signal.SIGINT, signal_handler) + signal.signal(signal.SIGTERM, signal_handler) + + LOGGER.info('Starting...') + + # Start metrics server + metrics_port = get_metrics_port() + start_http_server(metrics_port) + + # Get database instance + database = Database(get_database_backend()) + + # Get message broker instance + messagebroker = MessageBroker(get_messagebroker_backend()) + + # Starting context service + grpc_service = ContextService(database, messagebroker) + grpc_service.start() + + rest_server = RestServer() + for endpoint_name, resource_class, resource_url in RESOURCES: + rest_server.add_resource(resource_class, resource_url, endpoint=endpoint_name, resource_class_args=(database,)) + rest_server.start() + + populate_fake_data = get_setting('POPULATE_FAKE_DATA', default=POPULATE_FAKE_DATA) + if isinstance(populate_fake_data, str): populate_fake_data = (populate_fake_data.upper() in {'T', '1', 'TRUE'}) + if populate_fake_data: + LOGGER.info('Populating fake data...') + populate(host='127.0.0.1', port=grpc_service.bind_port) + LOGGER.info('Fake Data populated') + + # Wait for Ctrl+C or termination signal + while not terminate.wait(timeout=0.1): pass + + LOGGER.info('Terminating...') + grpc_service.stop() + rest_server.shutdown() + rest_server.join() + + LOGGER.info('Bye') + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/context/service/_old_code/test_unitary.py b/src/context/service/_old_code/test_unitary.py new file mode 100644 index 000000000..04e054aad --- /dev/null +++ b/src/context/service/_old_code/test_unitary.py @@ -0,0 +1,1450 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +# pylint: disable=too-many-lines +import copy, grpc, logging, os, pytest, requests, time, urllib +from typing import Tuple +from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID, ServiceNameEnum +from common.Settings import ( + ENVVAR_SUFIX_SERVICE_HOST, ENVVAR_SUFIX_SERVICE_PORT_GRPC, ENVVAR_SUFIX_SERVICE_PORT_HTTP, get_env_var_name, + get_service_baseurl_http, get_service_port_grpc, get_service_port_http) +from context.service.Database import Database +from common.message_broker.Factory import get_messagebroker_backend, BackendEnum as MessageBrokerBackendEnum +from common.message_broker.MessageBroker import MessageBroker +from common.proto.context_pb2 import ( + Connection, ConnectionEvent, ConnectionId, Context, ContextEvent, ContextId, Device, DeviceEvent, DeviceId, + DeviceOperationalStatusEnum, Empty, EventTypeEnum, Link, LinkEvent, LinkId, Service, ServiceEvent, ServiceId, + ServiceStatusEnum, ServiceTypeEnum, Topology, TopologyEvent, TopologyId) +from common.proto.policy_pb2 import (PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule) +from common.type_checkers.Assertions import ( + validate_connection, validate_connection_ids, validate_connections, validate_context, validate_context_ids, + validate_contexts, validate_device, validate_device_ids, validate_devices, validate_link, validate_link_ids, + validate_links, validate_service, validate_service_ids, validate_services, validate_topologies, validate_topology, + validate_topology_ids) +from context.client.ContextClient import ContextClient +from context.client.EventsCollector import EventsCollector +from context.service.database.Tools import ( + FASTHASHER_DATA_ACCEPTED_FORMAT, FASTHASHER_ITEM_ACCEPTED_FORMAT, fast_hasher) +from context.service.grpc_server.ContextService import ContextService +from context.service._old_code.Populate import populate +from context.service.rest_server.RestServer import RestServer +from context.service.rest_server.Resources import RESOURCES +from requests import Session +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker +from context.service.database._Base import Base + +from .Objects import ( + CONNECTION_R1_R3, CONNECTION_R1_R3_ID, CONNECTION_R1_R3_UUID, CONTEXT, CONTEXT_ID, DEVICE_R1, DEVICE_R1_ID, + DEVICE_R1_UUID, DEVICE_R2, DEVICE_R2_ID, DEVICE_R2_UUID, DEVICE_R3, DEVICE_R3_ID, DEVICE_R3_UUID, LINK_R1_R2, + LINK_R1_R2_ID, LINK_R1_R2_UUID, SERVICE_R1_R2, SERVICE_R1_R2_ID, SERVICE_R1_R2_UUID, SERVICE_R1_R3, + SERVICE_R1_R3_ID, SERVICE_R1_R3_UUID, SERVICE_R2_R3, SERVICE_R2_R3_ID, SERVICE_R2_R3_UUID, TOPOLOGY, TOPOLOGY_ID, + POLICY_RULE, POLICY_RULE_ID, POLICY_RULE_UUID) + +LOGGER = logging.getLogger(__name__) +LOGGER.setLevel(logging.DEBUG) + +LOCAL_HOST = '127.0.0.1' +GRPC_PORT = 10000 + int(get_service_port_grpc(ServiceNameEnum.CONTEXT)) # avoid privileged ports +HTTP_PORT = 10000 + int(get_service_port_http(ServiceNameEnum.CONTEXT)) # avoid privileged ports + +os.environ[get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_HOST )] = str(LOCAL_HOST) +os.environ[get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(GRPC_PORT) +os.environ[get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_HTTP)] = str(HTTP_PORT) + +DEFAULT_REDIS_SERVICE_HOST = LOCAL_HOST +DEFAULT_REDIS_SERVICE_PORT = 6379 +DEFAULT_REDIS_DATABASE_ID = 0 + +REDIS_CONFIG = { + 'REDIS_SERVICE_HOST': os.environ.get('REDIS_SERVICE_HOST', DEFAULT_REDIS_SERVICE_HOST), + 'REDIS_SERVICE_PORT': os.environ.get('REDIS_SERVICE_PORT', DEFAULT_REDIS_SERVICE_PORT), + 'REDIS_DATABASE_ID' : os.environ.get('REDIS_DATABASE_ID', DEFAULT_REDIS_DATABASE_ID ), +} + +SCENARIOS = [ + ('all_sqlalchemy', {}, MessageBrokerBackendEnum.INMEMORY, {} ), + ('all_inmemory', DatabaseBackendEnum.INMEMORY, {}, MessageBrokerBackendEnum.INMEMORY, {} ) +# ('all_redis', DatabaseBackendEnum.REDIS, REDIS_CONFIG, MessageBrokerBackendEnum.REDIS, REDIS_CONFIG), +] + +@pytest.fixture(scope='session', ids=[str(scenario[0]) for scenario in SCENARIOS], params=SCENARIOS) +def context_s_mb(request) -> Tuple[Session, MessageBroker]: + name,db_session,mb_backend,mb_settings = request.param + msg = 'Running scenario {:s} db_session={:s}, mb_backend={:s}, mb_settings={:s}...' + LOGGER.info(msg.format(str(name), str(db_session), str(mb_backend.value), str(mb_settings))) + + db_uri = 'cockroachdb://root@10.152.183.111:26257/defaultdb?sslmode=disable' + LOGGER.debug('Connecting to DB: {}'.format(db_uri)) + + try: + engine = create_engine(db_uri) + except Exception as e: + LOGGER.error("Failed to connect to database.") + LOGGER.error(f"{e}") + return 1 + + Base.metadata.create_all(engine) + _session = sessionmaker(bind=engine, expire_on_commit=False) + + _message_broker = MessageBroker(get_messagebroker_backend(backend=mb_backend, **mb_settings)) + yield _session, _message_broker + _message_broker.terminate() + +@pytest.fixture(scope='session') +def context_service_grpc(context_s_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name + _service = ContextService(context_s_mb[0], context_s_mb[1]) + _service.start() + yield _service + _service.stop() +@pytest.fixture(scope='session') +def context_service_rest(context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name + database = context_db_mb[0] + _rest_server = RestServer() + for endpoint_name, resource_class, resource_url in RESOURCES: + _rest_server.add_resource(resource_class, resource_url, endpoint=endpoint_name, resource_class_args=(database,)) + _rest_server.start() + time.sleep(1) # bring time for the server to start + yield _rest_server + _rest_server.shutdown() + _rest_server.join() +@pytest.fixture(scope='session') +def context_client_grpc(context_service_grpc : ContextService): # pylint: disable=redefined-outer-name + _client = ContextClient() + yield _client + _client.close() +""" +def do_rest_request(url : str): + base_url = get_service_baseurl_http(ServiceNameEnum.CONTEXT) + request_url = 'http://{:s}:{:s}{:s}{:s}'.format(str(LOCAL_HOST), str(HTTP_PORT), str(base_url), url) + LOGGER.warning('Request: GET {:s}'.format(str(request_url))) + reply = requests.get(request_url) + LOGGER.warning('Reply: {:s}'.format(str(reply.text))) + assert reply.status_code == 200, 'Reply failed with code {}'.format(reply.status_code) + return reply.json() +""" + +"""# ----- Test gRPC methods ---------------------------------------------------------------------------------------------- +def test_grpc_context( + context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name + context_s_mb : Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name + Session = context_s_mb[0] + + database = Database(Session) + + # ----- Clean the database ----------------------------------------------------------------------------------------- + database.clear() + # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- + events_collector = EventsCollector(context_client_grpc) + events_collector.start() + + # ----- Get when the object does not exist ------------------------------------------------------------------------- + with pytest.raises(grpc.RpcError) as e: + context_client_grpc.GetContext(ContextId(**CONTEXT_ID)) + assert e.value.code() == grpc.StatusCode.NOT_FOUND + assert e.value.details() == 'Context({:s}) not found'.format(DEFAULT_CONTEXT_UUID) + + # ----- List when the object does not exist ------------------------------------------------------------------------ + response = context_client_grpc.ListContextIds(Empty()) + assert len(response.context_ids) == 0 + + response = context_client_grpc.ListContexts(Empty()) + assert len(response.contexts) == 0 + + # ----- Dump state of database before create the object ------------------------------------------------------------ + db_entries = database.dump_all() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 0 + + # ----- Create the object ------------------------------------------------------------------------------------------ + response = context_client_grpc.SetContext(Context(**CONTEXT)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + wrong_uuid = 'c97c4185-e1d1-4ea7-b6b9-afbf76cb61f4' + with pytest.raises(grpc.RpcError) as e: + WRONG_TOPOLOGY_ID = copy.deepcopy(TOPOLOGY_ID) + WRONG_TOPOLOGY_ID['context_id']['context_uuid']['uuid'] = wrong_uuid + WRONG_CONTEXT = copy.deepcopy(CONTEXT) + WRONG_CONTEXT['topology_ids'].append(WRONG_TOPOLOGY_ID) + context_client_grpc.SetContext(Context(**WRONG_CONTEXT)) + assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT + msg = 'request.topology_ids[0].context_id.context_uuid.uuid({}) is invalid; '\ + 'should be == request.context_id.context_uuid.uuid({})'.format(wrong_uuid, DEFAULT_CONTEXT_UUID) + assert e.value.details() == msg + + with pytest.raises(grpc.RpcError) as e: + WRONG_SERVICE_ID = copy.deepcopy(SERVICE_R1_R2_ID) + WRONG_SERVICE_ID['context_id']['context_uuid']['uuid'] = wrong_uuid + WRONG_CONTEXT = copy.deepcopy(CONTEXT) + WRONG_CONTEXT['service_ids'].append(WRONG_SERVICE_ID) + context_client_grpc.SetContext(Context(**WRONG_CONTEXT)) + assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT + msg = 'request.service_ids[0].context_id.context_uuid.uuid({}) is invalid; '\ + 'should be == request.context_id.context_uuid.uuid({})'.format(wrong_uuid, DEFAULT_CONTEXT_UUID) + assert e.value.details() == msg + + # ----- Check create event ----------------------------------------------------------------------------------------- + event = events_collector.get_event(block=True) + assert isinstance(event, ContextEvent) + assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE + assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # ----- Update the object ------------------------------------------------------------------------------------------ + response = context_client_grpc.SetContext(Context(**CONTEXT)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Check update event ----------------------------------------------------------------------------------------- + event = events_collector.get_event(block=True) + assert isinstance(event, ContextEvent) + assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Dump state of database after create/update the object ------------------------------------------------------ + db_entries = database.dump_all() + + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 1 + + # ----- Get when the object exists --------------------------------------------------------------------------------- + response = context_client_grpc.GetContext(ContextId(**CONTEXT_ID)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert len(response.topology_ids) == 0 + assert len(response.service_ids) == 0 + + # ----- List when the object exists -------------------------------------------------------------------------------- + response = context_client_grpc.ListContextIds(Empty()) + assert len(response.context_ids) == 1 + assert response.context_ids[0].context_uuid.uuid == DEFAULT_CONTEXT_UUID + + response = context_client_grpc.ListContexts(Empty()) + assert len(response.contexts) == 1 + assert response.contexts[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert len(response.contexts[0].topology_ids) == 0 + assert len(response.contexts[0].service_ids) == 0 + + # ----- Remove the object ------------------------------------------------------------------------------------------ + context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) + + # ----- Check remove event ----------------------------------------------------------------------------------------- + # event = events_collector.get_event(block=True) + # assert isinstance(event, ContextEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- + events_collector.stop() + + # ----- Dump state of database after remove the object ------------------------------------------------------------- + db_entries = database.dump_all() + + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 0 + + +def test_grpc_topology( + context_client_grpc: ContextClient, # pylint: disable=redefined-outer-name + context_s_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name + session = context_s_mb[0] + + database = Database(session) + + # ----- Clean the database ----------------------------------------------------------------------------------------- + database.clear() + + # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- + events_collector = EventsCollector(context_client_grpc) + events_collector.start() + + # ----- Prepare dependencies for the test and capture related events ----------------------------------------------- + response = context_client_grpc.SetContext(Context(**CONTEXT)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # event = events_collector.get_event(block=True) + # assert isinstance(event, ContextEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Get when the object does not exist ------------------------------------------------------------------------- + with pytest.raises(grpc.RpcError) as e: + context_client_grpc.GetTopology(TopologyId(**TOPOLOGY_ID)) + assert e.value.code() == grpc.StatusCode.NOT_FOUND + # assert e.value.details() == 'Topology({:s}/{:s}) not found'.format(DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID) + assert e.value.details() == 'Topology({:s}) not found'.format(DEFAULT_TOPOLOGY_UUID) + # ----- List when the object does not exist ------------------------------------------------------------------------ + response = context_client_grpc.ListTopologyIds(ContextId(**CONTEXT_ID)) + assert len(response.topology_ids) == 0 + response = context_client_grpc.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == 0 + + # ----- Dump state of database before create the object ------------------------------------------------------------ + db_entries = database.dump_all() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 1 + + # ----- Create the object ------------------------------------------------------------------------------------------ + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + CONTEXT_WITH_TOPOLOGY = copy.deepcopy(CONTEXT) + CONTEXT_WITH_TOPOLOGY['topology_ids'].append(TOPOLOGY_ID) + response = context_client_grpc.SetContext(Context(**CONTEXT_WITH_TOPOLOGY)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Check create event ----------------------------------------------------------------------------------------- + # events = events_collector.get_events(block=True, count=2) + + # assert isinstance(events[0], TopologyEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[0].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert events[0].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + # assert isinstance(events[1], ContextEvent) + # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + # assert events[1].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Update the object ------------------------------------------------------------------------------------------ + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + # ----- Check update event ----------------------------------------------------------------------------------------- + # event = events_collector.get_event(block=True) + # assert isinstance(event, TopologyEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + # assert event.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert event.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + # ----- Dump state of database after create/update the object ------------------------------------------------------ + db_entries = database.dump_all() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 2 + + # ----- Get when the object exists --------------------------------------------------------------------------------- + response = context_client_grpc.GetTopology(TopologyId(**TOPOLOGY_ID)) + assert response.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + assert len(response.device_ids) == 0 + assert len(response.link_ids) == 0 + + # ----- List when the object exists -------------------------------------------------------------------------------- + response = context_client_grpc.ListTopologyIds(ContextId(**CONTEXT_ID)) + assert len(response.topology_ids) == 1 + assert response.topology_ids[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.topology_ids[0].topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + response = context_client_grpc.ListTopologies(ContextId(**CONTEXT_ID)) + assert len(response.topologies) == 1 + assert response.topologies[0].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.topologies[0].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + assert len(response.topologies[0].device_ids) == 0 + assert len(response.topologies[0].link_ids) == 0 + + # ----- Remove the object ------------------------------------------------------------------------------------------ + context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID)) + context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) + + # ----- Check remove event ----------------------------------------------------------------------------------------- + # events = events_collector.get_events(block=True, count=2) + + # assert isinstance(events[0], TopologyEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[0].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert events[0].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + # assert isinstance(events[1], ContextEvent) + # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[1].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- + # events_collector.stop() + + # ----- Dump state of database after remove the object ------------------------------------------------------------- + db_entries = database.dump_all() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 0 + + +def test_grpc_device( + context_client_grpc: ContextClient, # pylint: disable=redefined-outer-name + context_s_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name + session = context_s_mb[0] + + database = Database(session) + + # ----- Clean the database ----------------------------------------------------------------------------------------- + database.clear() + + # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- + events_collector = EventsCollector(context_client_grpc) + events_collector.start() + + # ----- Prepare dependencies for the test and capture related events ----------------------------------------------- + response = context_client_grpc.SetContext(Context(**CONTEXT)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + events = events_collector.get_events(block=True, count=2) + + assert isinstance(events[0], ContextEvent) + assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + assert events[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + assert isinstance(events[1], TopologyEvent) + assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + assert events[1].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert events[1].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + # ----- Get when the object does not exist ------------------------------------------------------------------------- + with pytest.raises(grpc.RpcError) as e: + context_client_grpc.GetDevice(DeviceId(**DEVICE_R1_ID)) + assert e.value.code() == grpc.StatusCode.NOT_FOUND + assert e.value.details() == 'Device({:s}) not found'.format(DEVICE_R1_UUID) + + # ----- List when the object does not exist ------------------------------------------------------------------------ + response = context_client_grpc.ListDeviceIds(Empty()) + assert len(response.device_ids) == 0 + + response = context_client_grpc.ListDevices(Empty()) + assert len(response.devices) == 0 + + # ----- Dump state of database before create the object ------------------------------------------------------------ + db_entries = database.dump_all() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 2 + + # ----- Create the object ------------------------------------------------------------------------------------------ + with pytest.raises(grpc.RpcError) as e: + WRONG_DEVICE = copy.deepcopy(DEVICE_R1) + WRONG_DEVICE_UUID = '3f03c76d-31fb-47f5-9c1d-bc6b6bfa2d08' + WRONG_DEVICE['device_endpoints'][0]['endpoint_id']['device_id']['device_uuid']['uuid'] = WRONG_DEVICE_UUID + context_client_grpc.SetDevice(Device(**WRONG_DEVICE)) + assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT + msg = 'request.device_endpoints[0].device_id.device_uuid.uuid({}) is invalid; '\ + 'should be == request.device_id.device_uuid.uuid({})'.format(WRONG_DEVICE_UUID, DEVICE_R1_UUID) + assert e.value.details() == msg + response = context_client_grpc.SetDevice(Device(**DEVICE_R1)) + assert response.device_uuid.uuid == DEVICE_R1_UUID + + # ----- Check create event ----------------------------------------------------------------------------------------- + # event = events_collector.get_event(block=True) + # assert isinstance(event, DeviceEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert event.device_id.device_uuid.uuid == DEVICE_R1_UUID + + # ----- Update the object ------------------------------------------------------------------------------------------ + response = context_client_grpc.SetDevice(Device(**DEVICE_R1)) + assert response.device_uuid.uuid == DEVICE_R1_UUID + + # ----- Check update event ----------------------------------------------------------------------------------------- + # event = events_collector.get_event(block=True) + # assert isinstance(event, DeviceEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + # assert event.device_id.device_uuid.uuid == DEVICE_R1_UUID + + # ----- Dump state of database after create/update the object ------------------------------------------------------ + db_entries = database.dump_all() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 47 + + # ----- Get when the object exists --------------------------------------------------------------------------------- + response = context_client_grpc.GetDevice(DeviceId(**DEVICE_R1_ID)) + assert response.device_id.device_uuid.uuid == DEVICE_R1_UUID + assert response.device_type == 'packet-router' + assert len(response.device_config.config_rules) == 3 + assert response.device_operational_status == DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED + assert len(response.device_drivers) == 1 + assert len(response.device_endpoints) == 3 + + # ----- List when the object exists -------------------------------------------------------------------------------- + response = context_client_grpc.ListDeviceIds(Empty()) + assert len(response.device_ids) == 1 + assert response.device_ids[0].device_uuid.uuid == DEVICE_R1_UUID + + response = context_client_grpc.ListDevices(Empty()) + assert len(response.devices) == 1 + assert response.devices[0].device_id.device_uuid.uuid == DEVICE_R1_UUID + assert response.devices[0].device_type == 'packet-router' + assert len(response.devices[0].device_config.config_rules) == 3 + assert response.devices[0].device_operational_status == DeviceOperationalStatusEnum.DEVICEOPERATIONALSTATUS_DISABLED + assert len(response.devices[0].device_drivers) == 1 + assert len(response.devices[0].device_endpoints) == 3 + + # ----- Create object relation ------------------------------------------------------------------------------------- + TOPOLOGY_WITH_DEVICE = copy.deepcopy(TOPOLOGY) + TOPOLOGY_WITH_DEVICE['device_ids'].append(DEVICE_R1_ID) + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY_WITH_DEVICE)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + # ----- Check update event ----------------------------------------------------------------------------------------- + # event = events_collector.get_event(block=True) + # assert isinstance(event, TopologyEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + # assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + # ----- Check relation was created --------------------------------------------------------------------------------- + response = context_client_grpc.GetTopology(TopologyId(**TOPOLOGY_ID)) + assert response.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + assert len(response.device_ids) == 1 + assert response.device_ids[0].device_uuid.uuid == DEVICE_R1_UUID + assert len(response.link_ids) == 0 + + # ----- Dump state of database after creating the object relation -------------------------------------------------- + db_entries = database.dump_all() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 47 + + # ----- Remove the object -------------------------------ro----------------------------------------------------------- + context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R1_ID)) + context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID)) + context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) + + # ----- Check remove event ----------------------------------------------------------------------------------------- + # events = events_collector.get_events(block=True, count=3) + + # assert isinstance(events[0], DeviceEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[0].device_id.device_uuid.uuid == DEVICE_R1_UUID + + # assert isinstance(events[1], TopologyEvent) + # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[1].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert events[1].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + # assert isinstance(events[2], ContextEvent) + # assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[2].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- + # events_collector.stop() + + # ----- Dump state of database after remove the object ------------------------------------------------------------- + db_entries = database.dump_all() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 0 + + +def test_grpc_link( + context_client_grpc: ContextClient, # pylint: disable=redefined-outer-name + context_s_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name + session = context_s_mb[0] + + database = Database(session) + + # ----- Clean the database ----------------------------------------------------------------------------------------- + database.clear() + + # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- + events_collector = EventsCollector(context_client_grpc) + events_collector.start() + + # ----- Prepare dependencies for the test and capture related events ----------------------------------------------- + response = context_client_grpc.SetContext(Context(**CONTEXT)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + response = context_client_grpc.SetDevice(Device(**DEVICE_R1)) + assert response.device_uuid.uuid == DEVICE_R1_UUID + + response = context_client_grpc.SetDevice(Device(**DEVICE_R2)) + assert response.device_uuid.uuid == DEVICE_R2_UUID + # events = events_collector.get_events(block=True, count=4) + + # assert isinstance(events[0], ContextEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # + # assert isinstance(events[1], TopologyEvent) + # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[1].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert events[1].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + # + # assert isinstance(events[2], DeviceEvent) + # assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[2].device_id.device_uuid.uuid == DEVICE_R1_UUID + # + # assert isinstance(events[3], DeviceEvent) + # assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[3].device_id.device_uuid.uuid == DEVICE_R2_UUID + + # ----- Get when the object does not exist ------------------------------------------------------------------------- + with pytest.raises(grpc.RpcError) as e: + context_client_grpc.GetLink(LinkId(**LINK_R1_R2_ID)) + assert e.value.code() == grpc.StatusCode.NOT_FOUND + assert e.value.details() == 'Link({:s}) not found'.format(LINK_R1_R2_UUID) + + # ----- List when the object does not exist ------------------------------------------------------------------------ + response = context_client_grpc.ListLinkIds(Empty()) + assert len(response.link_ids) == 0 + + response = context_client_grpc.ListLinks(Empty()) + assert len(response.links) == 0 + + # ----- Dump state of database before create the object ------------------------------------------------------------ + db_entries = database.dump_all() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 80 + + # ----- Create the object ------------------------------------------------------------------------------------------ + response = context_client_grpc.SetLink(Link(**LINK_R1_R2)) + assert response.link_uuid.uuid == LINK_R1_R2_UUID + + # ----- Check create event ----------------------------------------------------------------------------------------- + # event = events_collector.get_event(block=True) + # assert isinstance(event, LinkEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert event.link_id.link_uuid.uuid == LINK_R1_R2_UUID + + # ----- Update the object ------------------------------------------------------------------------------------------ + response = context_client_grpc.SetLink(Link(**LINK_R1_R2)) + assert response.link_uuid.uuid == LINK_R1_R2_UUID + # ----- Check update event ----------------------------------------------------------------------------------------- + # event = events_collector.get_event(block=True) + # assert isinstance(event, LinkEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + # assert event.link_id.link_uuid.uuid == LINK_R1_R2_UUID + + # ----- Dump state of database after create/update the object ------------------------------------------------------ + db_entries = database.dump_all() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 88 + + # ----- Get when the object exists --------------------------------------------------------------------------------- + response = context_client_grpc.GetLink(LinkId(**LINK_R1_R2_ID)) + assert response.link_id.link_uuid.uuid == LINK_R1_R2_UUID + assert len(response.link_endpoint_ids) == 2 + + # ----- List when the object exists -------------------------------------------------------------------------------- + response = context_client_grpc.ListLinkIds(Empty()) + assert len(response.link_ids) == 1 + assert response.link_ids[0].link_uuid.uuid == LINK_R1_R2_UUID + + response = context_client_grpc.ListLinks(Empty()) + assert len(response.links) == 1 + assert response.links[0].link_id.link_uuid.uuid == LINK_R1_R2_UUID + + assert len(response.links[0].link_endpoint_ids) == 2 + + # ----- Create object relation ------------------------------------------------------------------------------------- + TOPOLOGY_WITH_LINK = copy.deepcopy(TOPOLOGY) + TOPOLOGY_WITH_LINK['link_ids'].append(LINK_R1_R2_ID) + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY_WITH_LINK)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + # ----- Check update event ----------------------------------------------------------------------------------------- + # event = events_collector.get_event(block=True) + # assert isinstance(event, TopologyEvent) + # assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + # assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + # ----- Check relation was created --------------------------------------------------------------------------------- + response = context_client_grpc.GetTopology(TopologyId(**TOPOLOGY_ID)) + assert response.topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + assert len(response.device_ids) == 2 + # assert response.device_ids[0].device_uuid.uuid == DEVICE_R1_UUID + # assert response.device_ids[1].device_uuid.uuid == DEVICE_R2_UUID + assert len(response.link_ids) == 1 + assert response.link_ids[0].link_uuid.uuid == LINK_R1_R2_UUID + + db_entries = database.dump_all() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 88 + + # ----- Remove the object ------------------------------------------------------------------------------------------ + context_client_grpc.RemoveLink(LinkId(**LINK_R1_R2_ID)) + context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R1_ID)) + context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R2_ID)) + context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID)) + context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) + + # ----- Check remove event ----------------------------------------------------------------------------------------- + # events = events_collector.get_events(block=True, count=5) + # + # assert isinstance(events[0], LinkEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[0].link_id.link_uuid.uuid == LINK_R1_R2_UUID + # + # assert isinstance(events[1], DeviceEvent) + # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[1].device_id.device_uuid.uuid == DEVICE_R1_UUID + # + # assert isinstance(events[2], DeviceEvent) + # assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[2].device_id.device_uuid.uuid == DEVICE_R2_UUID + # + # assert isinstance(events[3], TopologyEvent) + # assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[3].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert events[3].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + # + # assert isinstance(events[4], ContextEvent) + # assert events[4].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[4].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- + events_collector.stop() + + # ----- Dump state of database after remove the object ------------------------------------------------------------- + db_entries = database.dump_all() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 0 +""" + +def test_grpc_service( + context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name + context_s_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name + Session = context_s_mb[0] + # ----- Clean the database ----------------------------------------------------------------------------------------- + database = Database(Session) + database.clear() + + # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- + events_collector = EventsCollector(context_client_grpc) + events_collector.start() + + # ----- Prepare dependencies for the test and capture related events ----------------------------------------------- + response = context_client_grpc.SetContext(Context(**CONTEXT)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + response = context_client_grpc.SetDevice(Device(**DEVICE_R1)) + assert response.device_uuid.uuid == DEVICE_R1_UUID + + response = context_client_grpc.SetDevice(Device(**DEVICE_R2)) + assert response.device_uuid.uuid == DEVICE_R2_UUID + # events = events_collector.get_events(block=True, count=4) + # + # assert isinstance(events[0], ContextEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # + # assert isinstance(events[1], TopologyEvent) + # assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[1].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + # assert events[1].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + # + # assert isinstance(events[2], DeviceEvent) + # assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[2].device_id.device_uuid.uuid == DEVICE_R1_UUID + # + # assert isinstance(events[3], DeviceEvent) + # assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[3].device_id.device_uuid.uuid == DEVICE_R2_UUID + LOGGER.info('----------------') + + # ----- Get when the object does not exist ------------------------------------------------------------------------- + with pytest.raises(grpc.RpcError) as e: + context_client_grpc.GetService(ServiceId(**SERVICE_R1_R2_ID)) + assert e.value.code() == grpc.StatusCode.NOT_FOUND + assert e.value.details() == 'Service({:s}) not found'.format(SERVICE_R1_R2_UUID) + LOGGER.info('----------------') + + # ----- List when the object does not exist ------------------------------------------------------------------------ + response = context_client_grpc.ListServiceIds(ContextId(**CONTEXT_ID)) + assert len(response.service_ids) == 0 + LOGGER.info('----------------') + + response = context_client_grpc.ListServices(ContextId(**CONTEXT_ID)) + assert len(response.services) == 0 + LOGGER.info('----------------') + + # ----- Dump state of database before create the object ------------------------------------------------------------ + db_entries = database.dump_all() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(db_entry) + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 80 + + # ----- Create the object ------------------------------------------------------------------------------------------ + with pytest.raises(grpc.RpcError) as e: + WRONG_SERVICE = copy.deepcopy(SERVICE_R1_R2) + WRONG_SERVICE['service_endpoint_ids'][0]\ + ['topology_id']['context_id']['context_uuid']['uuid'] = 'ca1ea172-728f-441d-972c-feeae8c9bffc' + context_client_grpc.SetService(Service(**WRONG_SERVICE)) + assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT + msg = 'request.service_endpoint_ids[0].topology_id.context_id.context_uuid.uuid(ca1ea172-728f-441d-972c-feeae8c9bffc) is invalid; '\ + 'should be == request.service_id.context_id.context_uuid.uuid({:s})'.format(DEFAULT_CONTEXT_UUID) + assert e.value.details() == msg + + response = context_client_grpc.SetService(Service(**SERVICE_R1_R2)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.service_uuid.uuid == SERVICE_R1_R2_UUID + + CONTEXT_WITH_SERVICE = copy.deepcopy(CONTEXT) + CONTEXT_WITH_SERVICE['service_ids'].append(SERVICE_R1_R2_ID) + response = context_client_grpc.SetContext(Context(**CONTEXT_WITH_SERVICE)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Check create event ----------------------------------------------------------------------------------------- + events = events_collector.get_events(block=True, count=2) + + assert isinstance(events[0], ServiceEvent) + assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + assert events[0].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert events[0].service_id.service_uuid.uuid == SERVICE_R1_R2_UUID + + assert isinstance(events[1], ContextEvent) + assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + assert events[1].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Update the object ------------------------------------------------------------------------------------------ + response = context_client_grpc.SetService(Service(**SERVICE_R1_R2)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.service_uuid.uuid == SERVICE_R1_R2_UUID + + # ----- Check update event ----------------------------------------------------------------------------------------- + event = events_collector.get_event(block=True) + assert isinstance(event, ServiceEvent) + assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + assert event.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert event.service_id.service_uuid.uuid == SERVICE_R1_R2_UUID + + # ----- Dump state of database after create/update the object ------------------------------------------------------ + db_entries = context_database.dump() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 108 + + # ----- Get when the object exists --------------------------------------------------------------------------------- + response = context_client_grpc.GetService(ServiceId(**SERVICE_R1_R2_ID)) + assert response.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.service_id.service_uuid.uuid == SERVICE_R1_R2_UUID + assert response.service_type == ServiceTypeEnum.SERVICETYPE_L3NM + assert len(response.service_endpoint_ids) == 2 + assert len(response.service_constraints) == 2 + assert response.service_status.service_status == ServiceStatusEnum.SERVICESTATUS_PLANNED + assert len(response.service_config.config_rules) == 3 + + # ----- List when the object exists -------------------------------------------------------------------------------- + response = context_client_grpc.ListServiceIds(ContextId(**CONTEXT_ID)) + assert len(response.service_ids) == 1 + assert response.service_ids[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.service_ids[0].service_uuid.uuid == SERVICE_R1_R2_UUID + + response = context_client_grpc.ListServices(ContextId(**CONTEXT_ID)) + assert len(response.services) == 1 + assert response.services[0].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.services[0].service_id.service_uuid.uuid == SERVICE_R1_R2_UUID + assert response.services[0].service_type == ServiceTypeEnum.SERVICETYPE_L3NM + assert len(response.services[0].service_endpoint_ids) == 2 + assert len(response.services[0].service_constraints) == 2 + assert response.services[0].service_status.service_status == ServiceStatusEnum.SERVICESTATUS_PLANNED + assert len(response.services[0].service_config.config_rules) == 3 + + # ----- Remove the object ------------------------------------------------------------------------------------------ + context_client_grpc.RemoveService(ServiceId(**SERVICE_R1_R2_ID)) + context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R1_ID)) + context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R2_ID)) + context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID)) + context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) + + # ----- Check remove event ----------------------------------------------------------------------------------------- + events = events_collector.get_events(block=True, count=5) + + assert isinstance(events[0], ServiceEvent) + assert events[0].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert events[0].service_id.service_uuid.uuid == SERVICE_R1_R2_UUID + + assert isinstance(events[1], DeviceEvent) + assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + assert events[1].device_id.device_uuid.uuid == DEVICE_R1_UUID + + assert isinstance(events[2], DeviceEvent) + assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + assert events[2].device_id.device_uuid.uuid == DEVICE_R2_UUID + + assert isinstance(events[3], TopologyEvent) + assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + assert events[3].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert events[3].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + assert isinstance(events[4], ContextEvent) + assert events[4].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + assert events[4].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- + events_collector.stop() + + # ----- Dump state of database after remove the object ------------------------------------------------------------- + db_entries = context_database.dump() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 0 + + +""" + +def test_grpc_connection( + context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name + context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name + Session = context_s_mb[0] + + database = Database(Session) + + # ----- Clean the database ----------------------------------------------------------------------------------------- + database.clear() + + # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- + events_collector = EventsCollector(context_client_grpc) + events_collector.start() + + # ----- Prepare dependencies for the test and capture related events ----------------------------------------------- + response = context_client_grpc.SetContext(Context(**CONTEXT)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + response = context_client_grpc.SetTopology(Topology(**TOPOLOGY)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + response = context_client_grpc.SetDevice(Device(**DEVICE_R1)) + assert response.device_uuid.uuid == DEVICE_R1_UUID + + response = context_client_grpc.SetDevice(Device(**DEVICE_R2)) + assert response.device_uuid.uuid == DEVICE_R2_UUID + + response = context_client_grpc.SetDevice(Device(**DEVICE_R3)) + assert response.device_uuid.uuid == DEVICE_R3_UUID + + response = context_client_grpc.SetService(Service(**SERVICE_R1_R2)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.service_uuid.uuid == SERVICE_R1_R2_UUID + + CONTEXT_WITH_SERVICE = copy.deepcopy(CONTEXT) + CONTEXT_WITH_SERVICE['service_ids'].append(SERVICE_R1_R2_ID) + response = context_client_grpc.SetContext(Context(**CONTEXT_WITH_SERVICE)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + response = context_client_grpc.SetService(Service(**SERVICE_R2_R3)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.service_uuid.uuid == SERVICE_R2_R3_UUID + + CONTEXT_WITH_SERVICE = copy.deepcopy(CONTEXT) + CONTEXT_WITH_SERVICE['service_ids'].append(SERVICE_R2_R3_ID) + response = context_client_grpc.SetContext(Context(**CONTEXT_WITH_SERVICE)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + response = context_client_grpc.SetService(Service(**SERVICE_R1_R3)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.service_uuid.uuid == SERVICE_R1_R3_UUID + + CONTEXT_WITH_SERVICE = copy.deepcopy(CONTEXT) + CONTEXT_WITH_SERVICE['service_ids'].append(SERVICE_R1_R3_ID) + response = context_client_grpc.SetContext(Context(**CONTEXT_WITH_SERVICE)) + assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + events = events_collector.get_events(block=True, count=11) + + assert isinstance(events[0], ContextEvent) + assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + assert events[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + assert isinstance(events[1], TopologyEvent) + assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + assert events[1].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert events[1].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + assert isinstance(events[2], DeviceEvent) + assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + assert events[2].device_id.device_uuid.uuid == DEVICE_R1_UUID + + assert isinstance(events[3], DeviceEvent) + assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + assert events[3].device_id.device_uuid.uuid == DEVICE_R2_UUID + + assert isinstance(events[4], DeviceEvent) + assert events[4].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + assert events[4].device_id.device_uuid.uuid == DEVICE_R3_UUID + + assert isinstance(events[5], ServiceEvent) + assert events[5].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + assert events[5].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert events[5].service_id.service_uuid.uuid == SERVICE_R1_R2_UUID + + assert isinstance(events[6], ContextEvent) + assert events[6].event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + assert events[6].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + assert isinstance(events[7], ServiceEvent) + assert events[7].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + assert events[7].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert events[7].service_id.service_uuid.uuid == SERVICE_R2_R3_UUID + + assert isinstance(events[8], ContextEvent) + assert events[8].event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + assert events[8].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + assert isinstance(events[9], ServiceEvent) + assert events[9].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + assert events[9].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert events[9].service_id.service_uuid.uuid == SERVICE_R1_R3_UUID + + assert isinstance(events[10], ContextEvent) + assert events[10].event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + assert events[10].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Get when the object does not exist ------------------------------------------------------------------------- + with pytest.raises(grpc.RpcError) as e: + context_client_grpc.GetConnection(ConnectionId(**CONNECTION_R1_R3_ID)) + assert e.value.code() == grpc.StatusCode.NOT_FOUND + assert e.value.details() == 'Connection({:s}) not found'.format(CONNECTION_R1_R3_UUID) + + # ----- List when the object does not exist ------------------------------------------------------------------------ + response = context_client_grpc.ListConnectionIds(ServiceId(**SERVICE_R1_R3_ID)) + assert len(response.connection_ids) == 0 + + response = context_client_grpc.ListConnections(ServiceId(**SERVICE_R1_R3_ID)) + assert len(response.connections) == 0 + + # ----- Dump state of database before create the object ------------------------------------------------------------ + db_entries = context_database.dump() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 187 + + # ----- Create the object ------------------------------------------------------------------------------------------ + with pytest.raises(grpc.RpcError) as e: + WRONG_CONNECTION = copy.deepcopy(CONNECTION_R1_R3) + WRONG_CONNECTION['path_hops_endpoint_ids'][0]\ + ['topology_id']['context_id']['context_uuid']['uuid'] = 'wrong-context-uuid' + context_client_grpc.SetConnection(Connection(**WRONG_CONNECTION)) + assert e.value.code() == grpc.StatusCode.NOT_FOUND + # TODO: should we check that all endpoints belong to same topology? + # TODO: should we check that endpoints form links over the topology? + msg = 'EndPoint({:s}/{:s}:wrong-context-uuid/{:s}) not found'.format( + DEVICE_R1_UUID, WRONG_CONNECTION['path_hops_endpoint_ids'][0]['endpoint_uuid']['uuid'], DEFAULT_TOPOLOGY_UUID) + assert e.value.details() == msg + + response = context_client_grpc.SetConnection(Connection(**CONNECTION_R1_R3)) + assert response.connection_uuid.uuid == CONNECTION_R1_R3_UUID + + # ----- Check create event ----------------------------------------------------------------------------------------- + event = events_collector.get_event(block=True) + assert isinstance(event, ConnectionEvent) + assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE + assert event.connection_id.connection_uuid.uuid == CONNECTION_R1_R3_UUID + + # ----- Update the object ------------------------------------------------------------------------------------------ + response = context_client_grpc.SetConnection(Connection(**CONNECTION_R1_R3)) + assert response.connection_uuid.uuid == CONNECTION_R1_R3_UUID + + # ----- Check update event ----------------------------------------------------------------------------------------- + event = events_collector.get_event(block=True) + assert isinstance(event, ConnectionEvent) + assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + assert event.connection_id.connection_uuid.uuid == CONNECTION_R1_R3_UUID + + # ----- Dump state of database after create/update the object ------------------------------------------------------ + db_entries = context_database.dump() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 203 + + # ----- Get when the object exists --------------------------------------------------------------------------------- + response = context_client_grpc.GetConnection(ConnectionId(**CONNECTION_R1_R3_ID)) + assert response.connection_id.connection_uuid.uuid == CONNECTION_R1_R3_UUID + assert response.service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.service_id.service_uuid.uuid == SERVICE_R1_R3_UUID + assert len(response.path_hops_endpoint_ids) == 6 + assert len(response.sub_service_ids) == 2 + + # ----- List when the object exists -------------------------------------------------------------------------------- + response = context_client_grpc.ListConnectionIds(ServiceId(**SERVICE_R1_R3_ID)) + assert len(response.connection_ids) == 1 + assert response.connection_ids[0].connection_uuid.uuid == CONNECTION_R1_R3_UUID + + response = context_client_grpc.ListConnections(ServiceId(**SERVICE_R1_R3_ID)) + assert len(response.connections) == 1 + assert response.connections[0].connection_id.connection_uuid.uuid == CONNECTION_R1_R3_UUID + assert len(response.connections[0].path_hops_endpoint_ids) == 6 + assert len(response.connections[0].sub_service_ids) == 2 + + # ----- Remove the object ------------------------------------------------------------------------------------------ + context_client_grpc.RemoveConnection(ConnectionId(**CONNECTION_R1_R3_ID)) + context_client_grpc.RemoveService(ServiceId(**SERVICE_R1_R3_ID)) + context_client_grpc.RemoveService(ServiceId(**SERVICE_R2_R3_ID)) + context_client_grpc.RemoveService(ServiceId(**SERVICE_R1_R2_ID)) + context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R1_ID)) + context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R2_ID)) + context_client_grpc.RemoveDevice(DeviceId(**DEVICE_R3_ID)) + context_client_grpc.RemoveTopology(TopologyId(**TOPOLOGY_ID)) + context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) + + # ----- Check remove event ----------------------------------------------------------------------------------------- + events = events_collector.get_events(block=True, count=9) + + assert isinstance(events[0], ConnectionEvent) + assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + assert events[0].connection_id.connection_uuid.uuid == CONNECTION_R1_R3_UUID + + assert isinstance(events[1], ServiceEvent) + assert events[1].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + assert events[1].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert events[1].service_id.service_uuid.uuid == SERVICE_R1_R3_UUID + + assert isinstance(events[2], ServiceEvent) + assert events[2].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + assert events[2].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert events[2].service_id.service_uuid.uuid == SERVICE_R2_R3_UUID + + assert isinstance(events[3], ServiceEvent) + assert events[3].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + assert events[3].service_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert events[3].service_id.service_uuid.uuid == SERVICE_R1_R2_UUID + + assert isinstance(events[4], DeviceEvent) + assert events[4].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + assert events[4].device_id.device_uuid.uuid == DEVICE_R1_UUID + + assert isinstance(events[5], DeviceEvent) + assert events[5].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + assert events[5].device_id.device_uuid.uuid == DEVICE_R2_UUID + + assert isinstance(events[6], DeviceEvent) + assert events[6].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + assert events[6].device_id.device_uuid.uuid == DEVICE_R3_UUID + + assert isinstance(events[7], TopologyEvent) + assert events[7].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + assert events[7].topology_id.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert events[7].topology_id.topology_uuid.uuid == DEFAULT_TOPOLOGY_UUID + + assert isinstance(events[8], ContextEvent) + assert events[8].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + assert events[8].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- + events_collector.stop() + + # ----- Dump state of database after remove the object ------------------------------------------------------------- + db_entries = context_database.dump() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 0 + + +def test_grpc_policy( + context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name + context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name + context_database = context_db_mb[0] + + # ----- Clean the database ----------------------------------------------------------------------------------------- + context_database.clear_all() + + # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- + #events_collector = EventsCollector(context_client_grpc) + #events_collector.start() + + # ----- Get when the object does not exist ------------------------------------------------------------------------- + POLICY_ID = 'no-uuid' + DEFAULT_POLICY_ID = {'uuid': {'uuid': POLICY_ID}} + + with pytest.raises(grpc.RpcError) as e: + context_client_grpc.GetPolicyRule(PolicyRuleId(**DEFAULT_POLICY_ID)) + + assert e.value.code() == grpc.StatusCode.NOT_FOUND + assert e.value.details() == 'PolicyRule({:s}) not found'.format(POLICY_ID) + + # ----- List when the object does not exist ------------------------------------------------------------------------ + response = context_client_grpc.ListPolicyRuleIds(Empty()) + assert len(response.policyRuleIdList) == 0 + + response = context_client_grpc.ListPolicyRules(Empty()) + assert len(response.policyRules) == 0 + + # ----- Dump state of database before create the object ------------------------------------------------------------ + db_entries = context_database.dump() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 0 + + # ----- Create the object ------------------------------------------------------------------------------------------ + response = context_client_grpc.SetPolicyRule(PolicyRule(**POLICY_RULE)) + assert response.uuid.uuid == POLICY_RULE_UUID + + # ----- Check create event ----------------------------------------------------------------------------------------- + # events = events_collector.get_events(block=True, count=1) + # assert isinstance(events[0], PolicyEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_CREATE + # assert events[0].policy_id.uuid.uuid == POLICY_RULE_UUID + + # ----- Update the object ------------------------------------------------------------------------------------------ + response = context_client_grpc.SetPolicyRule(PolicyRule(**POLICY_RULE)) + assert response.uuid.uuid == POLICY_RULE_UUID + + # ----- Dump state of database after create/update the object ------------------------------------------------------ + db_entries = context_database.dump() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 2 + + # ----- Get when the object exists --------------------------------------------------------------------------------- + response = context_client_grpc.GetPolicyRule(PolicyRuleId(**POLICY_RULE_ID)) + assert response.device.policyRuleBasic.policyRuleId.uuid.uuid == POLICY_RULE_UUID + + # ----- List when the object exists -------------------------------------------------------------------------------- + response = context_client_grpc.ListPolicyRuleIds(Empty()) + assert len(response.policyRuleIdList) == 1 + assert response.policyRuleIdList[0].uuid.uuid == POLICY_RULE_UUID + + response = context_client_grpc.ListPolicyRules(Empty()) + assert len(response.policyRules) == 1 + + # ----- Remove the object ------------------------------------------------------------------------------------------ + context_client_grpc.RemovePolicyRule(PolicyRuleId(**POLICY_RULE_ID)) + + # ----- Check remove event ----------------------------------------------------------------------------------------- + # events = events_collector.get_events(block=True, count=2) + + # assert isinstance(events[0], PolicyEvent) + # assert events[0].event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + # assert events[0].policy_id.uuid.uuid == POLICY_RULE_UUID + + + # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- + # events_collector.stop() + + # ----- Dump state of database after remove the object ------------------------------------------------------------- + db_entries = context_database.dump() + LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + for db_entry in db_entries: + LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + LOGGER.info('-----------------------------------------------------------') + assert len(db_entries) == 0 + + + +# ----- Test REST API methods ------------------------------------------------------------------------------------------ + +def test_rest_populate_database( + context_db_mb : Tuple[Database, MessageBroker], # pylint: disable=redefined-outer-name + context_service_grpc : ContextService # pylint: disable=redefined-outer-name + ): + database = context_db_mb[0] + database.clear_all() + populate(LOCAL_HOST, GRPC_PORT) + +def test_rest_get_context_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/context_ids') + validate_context_ids(reply) + +def test_rest_get_contexts(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/contexts') + validate_contexts(reply) + +def test_rest_get_context(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}'.format(context_uuid)) + validate_context(reply) + +def test_rest_get_topology_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}/topology_ids'.format(context_uuid)) + validate_topology_ids(reply) + +def test_rest_get_topologies(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}/topologies'.format(context_uuid)) + validate_topologies(reply) + +def test_rest_get_topology(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + topology_uuid = urllib.parse.quote(DEFAULT_TOPOLOGY_UUID) + reply = do_rest_request('/context/{:s}/topology/{:s}'.format(context_uuid, topology_uuid)) + validate_topology(reply, num_devices=3, num_links=3) + +def test_rest_get_service_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}/service_ids'.format(context_uuid)) + validate_service_ids(reply) + +def test_rest_get_services(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}/services'.format(context_uuid)) + validate_services(reply) + +def test_rest_get_service(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + service_uuid = urllib.parse.quote(SERVICE_R1_R2_UUID, safe='') + reply = do_rest_request('/context/{:s}/service/{:s}'.format(context_uuid, service_uuid)) + validate_service(reply) + +def test_rest_get_slice_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}/slice_ids'.format(context_uuid)) + #validate_slice_ids(reply) + +def test_rest_get_slices(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + reply = do_rest_request('/context/{:s}/slices'.format(context_uuid)) + #validate_slices(reply) + +#def test_rest_get_slice(context_service_rest : RestServer): # pylint: disable=redefined-outer-name +# context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) +# slice_uuid = urllib.parse.quote(SLICE_R1_R2_UUID, safe='') +# reply = do_rest_request('/context/{:s}/slice/{:s}'.format(context_uuid, slice_uuid)) +# #validate_slice(reply) + +def test_rest_get_device_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/device_ids') + validate_device_ids(reply) + +def test_rest_get_devices(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/devices') + validate_devices(reply) + +def test_rest_get_device(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + device_uuid = urllib.parse.quote(DEVICE_R1_UUID, safe='') + reply = do_rest_request('/device/{:s}'.format(device_uuid)) + validate_device(reply) + +def test_rest_get_link_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/link_ids') + validate_link_ids(reply) + +def test_rest_get_links(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/links') + validate_links(reply) + +def test_rest_get_link(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + link_uuid = urllib.parse.quote(LINK_R1_R2_UUID, safe='') + reply = do_rest_request('/link/{:s}'.format(link_uuid)) + validate_link(reply) + +def test_rest_get_connection_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + service_uuid = urllib.parse.quote(SERVICE_R1_R3_UUID, safe='') + reply = do_rest_request('/context/{:s}/service/{:s}/connection_ids'.format(context_uuid, service_uuid)) + validate_connection_ids(reply) + +def test_rest_get_connections(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) + service_uuid = urllib.parse.quote(SERVICE_R1_R3_UUID, safe='') + reply = do_rest_request('/context/{:s}/service/{:s}/connections'.format(context_uuid, service_uuid)) + validate_connections(reply) + +def test_rest_get_connection(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + connection_uuid = urllib.parse.quote(CONNECTION_R1_R3_UUID, safe='') + reply = do_rest_request('/connection/{:s}'.format(connection_uuid)) + validate_connection(reply) + +def test_rest_get_policyrule_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/policyrule_ids') + #validate_policyrule_ids(reply) + +def test_rest_get_policyrules(context_service_rest : RestServer): # pylint: disable=redefined-outer-name + reply = do_rest_request('/policyrules') + #validate_policyrules(reply) + +#def test_rest_get_policyrule(context_service_rest : RestServer): # pylint: disable=redefined-outer-name +# policyrule_uuid = urllib.parse.quote(POLICYRULE_UUID, safe='') +# reply = do_rest_request('/policyrule/{:s}'.format(policyrule_uuid)) +# #validate_policyrule(reply) + + +# ----- Test misc. Context internal tools ------------------------------------------------------------------------------ + +def test_tools_fast_string_hasher(): + with pytest.raises(TypeError) as e: + fast_hasher(27) + assert str(e.value) == "data(27) must be " + FASTHASHER_DATA_ACCEPTED_FORMAT + ", found " + + with pytest.raises(TypeError) as e: + fast_hasher({27}) + assert str(e.value) == "data({27}) must be " + FASTHASHER_DATA_ACCEPTED_FORMAT + ", found " + + with pytest.raises(TypeError) as e: + fast_hasher({'27'}) + assert str(e.value) == "data({'27'}) must be " + FASTHASHER_DATA_ACCEPTED_FORMAT + ", found " + + with pytest.raises(TypeError) as e: + fast_hasher([27]) + assert str(e.value) == "data[0](27) must be " + FASTHASHER_ITEM_ACCEPTED_FORMAT + ", found " + + fast_hasher('hello-world') + fast_hasher('hello-world'.encode('UTF-8')) + fast_hasher(['hello', 'world']) + fast_hasher(('hello', 'world')) + fast_hasher(['hello'.encode('UTF-8'), 'world'.encode('UTF-8')]) + fast_hasher(('hello'.encode('UTF-8'), 'world'.encode('UTF-8'))) +""" \ No newline at end of file diff --git a/src/context/service/database/Base.py b/src/context/service/database/Base.py deleted file mode 100644 index c64447da1..000000000 --- a/src/context/service/database/Base.py +++ /dev/null @@ -1,2 +0,0 @@ -from sqlalchemy.ext.declarative import declarative_base -Base = declarative_base() diff --git a/src/context/service/database/ConfigModel.py b/src/context/service/database/ConfigModel.py index 0de91c2df..5f7111981 100644 --- a/src/context/service/database/ConfigModel.py +++ b/src/context/service/database/ConfigModel.py @@ -19,7 +19,7 @@ from common.proto.context_pb2 import ConfigActionEnum from common.tools.grpc.Tools import grpc_message_to_json_string from sqlalchemy import Column, ForeignKey, INTEGER, CheckConstraint, Enum, String from sqlalchemy.dialects.postgresql import UUID, ARRAY -from context.service.database.Base import Base +from context.service.database._Base import Base from sqlalchemy.orm import relationship from context.service.Database import Database diff --git a/src/context/service/database/ConnectionModel.py b/src/context/service/database/ConnectionModel.py index 1147f3859..e780ccb68 100644 --- a/src/context/service/database/ConnectionModel.py +++ b/src/context/service/database/ConnectionModel.py @@ -36,7 +36,7 @@ from .ConstraintModel import ConstraintsModel from .ContextModel import ContextModel from .Tools import grpc_to_enum from sqlalchemy.dialects.postgresql import UUID -from context.service.database.Base import Base +from context.service.database._Base import Base import enum LOGGER = logging.getLogger(__name__) diff --git a/src/context/service/database/ConstraintModel.py b/src/context/service/database/ConstraintModel.py index cf3b5f0d7..30d900300 100644 --- a/src/context/service/database/ConstraintModel.py +++ b/src/context/service/database/ConstraintModel.py @@ -22,7 +22,7 @@ from .EndPointModel import EndPointModel from .Tools import fast_hasher, remove_dict_key from sqlalchemy import Column, ForeignKey, String, Float, CheckConstraint, Integer, Boolean, Enum from sqlalchemy.dialects.postgresql import UUID -from context.service.database.Base import Base +from context.service.database._Base import Base import enum LOGGER = logging.getLogger(__name__) diff --git a/src/context/service/database/ContextModel.py b/src/context/service/database/ContextModel.py index cde774fe4..46f0741e5 100644 --- a/src/context/service/database/ContextModel.py +++ b/src/context/service/database/ContextModel.py @@ -13,29 +13,27 @@ # limitations under the License. import logging -from typing import Dict, List -from sqlalchemy import Column +from typing import Dict +from sqlalchemy import Column, String from sqlalchemy.dialects.postgresql import UUID -from context.service.database.Base import Base -from sqlalchemy.orm import relationship - +from ._Base import _Base +#from sqlalchemy.orm import relationship LOGGER = logging.getLogger(__name__) - -class ContextModel(Base): - __tablename__ = 'Context' +class ContextModel(_Base): + __tablename__ = 'context' context_uuid = Column(UUID(as_uuid=False), primary_key=True) + context_name = Column(String(), nullable=False) - # Relationships - topology = relationship("TopologyModel", back_populates="context") + #topology = relationship('TopologyModel', back_populates='context') def dump_id(self) -> Dict: return {'context_uuid': {'uuid': self.context_uuid}} - @staticmethod - def main_pk_name(): - return 'context_uuid' + #@staticmethod + #def main_pk_name(): + # return 'context_uuid' """ def dump_service_ids(self) -> List[Dict]: @@ -50,8 +48,7 @@ class ContextModel(Base): """ def dump(self, include_services=True, include_topologies=True) -> Dict: # pylint: disable=arguments-differ - result = {'context_id': self.dump_id()} + result = {'context_id': self.dump_id(), 'name': self.context_name} # if include_services: result['service_ids'] = self.dump_service_ids() # if include_topologies: result['topology_ids'] = self.dump_topology_ids() return result - diff --git a/src/context/service/database/DeviceModel.py b/src/context/service/database/DeviceModel.py index cb4517e68..cb568e123 100644 --- a/src/context/service/database/DeviceModel.py +++ b/src/context/service/database/DeviceModel.py @@ -20,7 +20,7 @@ from common.orm.backend.Tools import key_to_str from common.proto.context_pb2 import DeviceDriverEnum, DeviceOperationalStatusEnum from sqlalchemy import Column, ForeignKey, String, Enum from sqlalchemy.dialects.postgresql import UUID, ARRAY -from context.service.database.Base import Base +from context.service.database._Base import Base from sqlalchemy.orm import relationship from .Tools import grpc_to_enum diff --git a/src/context/service/database/EndPointModel.py b/src/context/service/database/EndPointModel.py index 540453970..38214aa9b 100644 --- a/src/context/service/database/EndPointModel.py +++ b/src/context/service/database/EndPointModel.py @@ -21,7 +21,7 @@ from common.proto.context_pb2 import EndPointId from .KpiSampleType import ORM_KpiSampleTypeEnum, grpc_to_enum__kpi_sample_type from sqlalchemy import Column, ForeignKey, String, Enum, ForeignKeyConstraint from sqlalchemy.dialects.postgresql import UUID -from context.service.database.Base import Base +from context.service.database._Base import Base from sqlalchemy.orm import relationship LOGGER = logging.getLogger(__name__) diff --git a/src/context/service/database/LinkModel.py b/src/context/service/database/LinkModel.py index 025709dfd..6b768d1b7 100644 --- a/src/context/service/database/LinkModel.py +++ b/src/context/service/database/LinkModel.py @@ -16,7 +16,7 @@ import logging, operator from typing import Dict, List from sqlalchemy import Column, ForeignKey from sqlalchemy.dialects.postgresql import UUID -from context.service.database.Base import Base +from context.service.database._Base import Base from sqlalchemy.orm import relationship LOGGER = logging.getLogger(__name__) diff --git a/src/context/service/database/RelationModels.py b/src/context/service/database/RelationModels.py index e69feadc4..61e05db0e 100644 --- a/src/context/service/database/RelationModels.py +++ b/src/context/service/database/RelationModels.py @@ -15,7 +15,7 @@ import logging from sqlalchemy import Column, ForeignKey from sqlalchemy.dialects.postgresql import UUID -from context.service.database.Base import Base +from context.service.database._Base import Base LOGGER = logging.getLogger(__name__) # diff --git a/src/context/service/database/ServiceModel.py b/src/context/service/database/ServiceModel.py index 8f358be52..20e10ddd5 100644 --- a/src/context/service/database/ServiceModel.py +++ b/src/context/service/database/ServiceModel.py @@ -22,7 +22,7 @@ from .ConstraintModel import ConstraintsModel from .ContextModel import ContextModel from .Tools import grpc_to_enum from sqlalchemy.dialects.postgresql import UUID -from context.service.database.Base import Base +from context.service.database._Base import Base import enum LOGGER = logging.getLogger(__name__) diff --git a/src/context/service/database/TopologyModel.py b/src/context/service/database/TopologyModel.py index 063a1f511..0a5698163 100644 --- a/src/context/service/database/TopologyModel.py +++ b/src/context/service/database/TopologyModel.py @@ -17,7 +17,7 @@ from typing import Dict, List from sqlalchemy.orm import relationship from sqlalchemy import Column, ForeignKey from sqlalchemy.dialects.postgresql import UUID -from context.service.database.Base import Base +from context.service.database._Base import Base LOGGER = logging.getLogger(__name__) class TopologyModel(Base): diff --git a/src/context/service/database/_Base.py b/src/context/service/database/_Base.py new file mode 100644 index 000000000..49269be08 --- /dev/null +++ b/src/context/service/database/_Base.py @@ -0,0 +1,22 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 sqlalchemy +from sqlalchemy.orm import declarative_base + +_Base = declarative_base() + +def rebuild_database(db_engine : sqlalchemy.engine.Engine, drop_if_exists : bool = False): + if drop_if_exists: _Base.metadata.drop_all(db_engine) + _Base.metadata.create_all(db_engine) diff --git a/src/context/service/database/__init__.py b/src/context/service/database/__init__.py index 70a332512..27b5f5dd2 100644 --- a/src/context/service/database/__init__.py +++ b/src/context/service/database/__init__.py @@ -12,3 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. +from ._Base import _Base, rebuild_database diff --git a/src/context/service/grpc_server/ContextServiceServicerImpl.py b/src/context/service/grpc_server/ContextServiceServicerImpl.py deleted file mode 100644 index 4d7f06463..000000000 --- a/src/context/service/grpc_server/ContextServiceServicerImpl.py +++ /dev/null @@ -1,1213 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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 uuid - -import grpc, json, logging, operator, threading -from typing import Iterator, List, Set, Tuple, Union -from common.message_broker.MessageBroker import MessageBroker -from context.service.Database import Database -from common.tools.grpc.Tools import grpc_message_to_json_string - -from common.proto.context_pb2 import ( - Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, - Context, ContextEvent, ContextId, ContextIdList, ContextList, - Device, DeviceEvent, DeviceId, DeviceIdList, DeviceList, - Empty, EventTypeEnum, - Link, LinkEvent, LinkId, LinkIdList, LinkList, - Service, ServiceEvent, ServiceId, ServiceIdList, ServiceList, - Slice, SliceEvent, SliceId, SliceIdList, SliceList, - Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList, - ConfigActionEnum, Constraint) -from common.proto.policy_pb2 import (PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule) -from common.proto.context_pb2_grpc import ContextServiceServicer -from common.proto.context_policy_pb2_grpc import ContextPolicyServiceServicer -from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method -from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException -from sqlalchemy.orm import Session, contains_eager, selectinload -from common.rpc_method_wrapper.ServiceExceptions import NotFoundException -from context.service.database.ConfigModel import grpc_config_rules_to_raw -from context.service.database.DeviceModel import DeviceModel, grpc_to_enum__device_operational_status, set_drivers, grpc_to_enum__device_driver, DriverModel -from context.service.database.ConfigModel import ConfigModel, ORM_ConfigActionEnum, ConfigRuleModel - -from common.orm.backend.Tools import key_to_str - -from ..database.KpiSampleType import grpc_to_enum__kpi_sample_type - -""" -from context.service.database.ConnectionModel import ConnectionModel, set_path -from context.service.database.ConstraintModel import set_constraints -from common.tools.grpc.Tools import grpc_message_to_json -from context.service.database.ConfigModel import update_config -from context.service.database.ConnectionModel import ConnectionModel, set_path -from context.service.database.ConstraintModel import set_constraints -from context.service.database.ContextModel import ContextModel -from context.service.database.PolicyRuleModel import PolicyRuleModel -from context.service.database.DeviceModel import DeviceModel, grpc_to_enum__device_operational_status, set_drivers -from context.service.database.EndPointModel import EndPointModel, set_kpi_sample_types -from context.service.database.Events import notify_event -from context.service.database.RelationModels import ( - ConnectionSubServiceModel, LinkEndPointModel, ServiceEndPointModel, SliceEndPointModel, SliceServiceModel, - SliceSubSliceModel, TopologyDeviceModel, TopologyLinkModel) -from context.service.database.ServiceModel import ( - ServiceModel, grpc_to_enum__service_status, grpc_to_enum__service_type) -from context.service.database.SliceModel import SliceModel, grpc_to_enum__slice_status -from context.service.database.TopologyModel import TopologyModel -""" -from context.service.database.ContextModel import ContextModel -from context.service.database.TopologyModel import TopologyModel -from context.service.database.Events import notify_event -from context.service.database.EndPointModel import EndPointModel -from context.service.database.EndPointModel import KpiSampleTypeModel -from context.service.database.LinkModel import LinkModel -from context.service.database.ServiceModel import ServiceModel -from context.service.database.ConstraintModel import ConstraintModel, ConstraintsModel, Union_ConstraintModel, CONSTRAINT_PARSERS -from context.service.database.RelationModels import (TopologyDeviceModel, TopologyLinkModel, LinkEndPointModel) - -from .Constants import ( - CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE, - TOPIC_TOPOLOGY) - -LOGGER = logging.getLogger(__name__) - -SERVICE_NAME = 'Context' -METHOD_NAMES = [ - 'ListConnectionIds', 'ListConnections', 'GetConnection', 'SetConnection', 'RemoveConnection', 'GetConnectionEvents', - 'ListContextIds', 'ListContexts', 'GetContext', 'SetContext', 'RemoveContext', 'GetContextEvents', - 'ListTopologyIds', 'ListTopologies', 'GetTopology', 'SetTopology', 'RemoveTopology', 'GetTopologyEvents', - 'ListDeviceIds', 'ListDevices', 'GetDevice', 'SetDevice', 'RemoveDevice', 'GetDeviceEvents', - 'ListLinkIds', 'ListLinks', 'GetLink', 'SetLink', 'RemoveLink', 'GetLinkEvents', - 'ListServiceIds', 'ListServices', 'GetService', 'SetService', 'RemoveService', 'GetServiceEvents', - 'ListSliceIds', 'ListSlices', 'GetSlice', 'SetSlice', 'RemoveSlice', 'GetSliceEvents', - 'ListPolicyRuleIds', 'ListPolicyRules', 'GetPolicyRule', 'SetPolicyRule', 'RemovePolicyRule', - 'UnsetService', 'UnsetSlice', -] -METRICS = create_metrics(SERVICE_NAME, METHOD_NAMES) - -class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceServicer): - #def __init__(self, session : Session, messagebroker : MessageBroker): - def __init__(self, database : Database, messagebroker : MessageBroker): - LOGGER.debug('Creating Servicer...') - self.lock = threading.Lock() - self.session = session - self.database = Database(session) - self.messagebroker = messagebroker - LOGGER.debug('Servicer Created') - - - # ----- Context ---------------------------------------------------------------------------------------------------- - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListContextIds(self, request: Empty, context : grpc.ServicerContext) -> ContextIdList: - with self.session() as session: - result = session.query(ContextModel).all() - - return ContextIdList(context_ids=[row.dump_id() for row in result]) - - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListContexts(self, request: Empty, context : grpc.ServicerContext) -> ContextList: - with self.session() as session: - result = session.query(ContextModel).all() - - return ContextList(contexts=[row.dump() for row in result]) - - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetContext(self, request: ContextId, context : grpc.ServicerContext) -> Context: - context_uuid = request.context_uuid.uuid - with self.session() as session: - result = session.query(ContextModel).filter_by(context_uuid=context_uuid).one_or_none() - - if not result: - raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) - - return Context(**result.dump()) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def SetContext(self, request: Context, context : grpc.ServicerContext) -> ContextId: - context_uuid = request.context_id.context_uuid.uuid - - for i, topology_id in enumerate(request.topology_ids): - topology_context_uuid = topology_id.context_id.context_uuid.uuid - if topology_context_uuid != context_uuid: - raise InvalidArgumentException( - 'request.topology_ids[{:d}].context_id.context_uuid.uuid'.format(i), topology_context_uuid, - ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)]) - - for i, service_id in enumerate(request.service_ids): - service_context_uuid = service_id.context_id.context_uuid.uuid - if service_context_uuid != context_uuid: - raise InvalidArgumentException( - 'request.service_ids[{:d}].context_id.context_uuid.uuid'.format(i), service_context_uuid, - ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)]) - - context_add = ContextModel(context_uuid=context_uuid) - - updated = True - with self.session() as session: - result = session.query(ContextModel).filter_by(context_uuid=context_uuid).all() - if not result: - updated = False - session.merge(context_add) - session.commit() - - - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_context_id = context_add.dump_id() - notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': dict_context_id}) - return ContextId(**context_add.dump_id()) - - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def RemoveContext(self, request: ContextId, context : grpc.ServicerContext) -> Empty: - context_uuid = request.context_uuid.uuid - - with self.session() as session: - result = session.query(ContextModel).filter_by(context_uuid=context_uuid).one_or_none() - if not result: - return Empty() - session.query(ContextModel).filter_by(context_uuid=context_uuid).delete() - session.commit() - event_type = EventTypeEnum.EVENTTYPE_REMOVE - notify_event(self.messagebroker, TOPIC_CONTEXT, event_type, {'context_id': result.dump_id()}) - return Empty() - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetContextEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ContextEvent]: - for message in self.messagebroker.consume({TOPIC_CONTEXT}, consume_timeout=CONSUME_TIMEOUT): - yield ContextEvent(**json.loads(message.content)) - - - # ----- Topology --------------------------------------------------------------------------------------------------- - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListTopologyIds(self, request: ContextId, context : grpc.ServicerContext) -> TopologyIdList: - context_uuid = request.context_uuid.uuid - - with self.session() as session: - result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by(context_uuid=context_uuid).one_or_none() - if not result: - raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) - - db_topologies = result.topology - return TopologyIdList(topology_ids=[db_topology.dump_id() for db_topology in db_topologies]) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListTopologies(self, request: ContextId, context : grpc.ServicerContext) -> TopologyList: - context_uuid = request.context_uuid.uuid - - with self.session() as session: - result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by( - context_uuid=context_uuid).one_or_none() - if not result: - raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) - - db_topologies = result.topology - return TopologyList(topologies=[db_topology.dump() for db_topology in db_topologies]) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology: - topology_uuid = request.topology_uuid.uuid - - result, dump = self.database.get_object(TopologyModel, topology_uuid, True) - with self.session() as session: - devs = None - links = None - - filt = {'topology_uuid': topology_uuid} - topology_devices = session.query(TopologyDeviceModel).filter_by(**filt).all() - if topology_devices: - devs = [] - for td in topology_devices: - filt = {'device_uuid': td.device_uuid} - devs.append(session.query(DeviceModel).filter_by(**filt).one()) - - filt = {'topology_uuid': topology_uuid} - topology_links = session.query(TopologyLinkModel).filter_by(**filt).all() - if topology_links: - links = [] - for tl in topology_links: - filt = {'link_uuid': tl.link_uuid} - links.append(session.query(LinkModel).filter_by(**filt).one()) - - return Topology(**result.dump(devs, links)) - - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def SetTopology(self, request: Topology, context : grpc.ServicerContext) -> TopologyId: - context_uuid = request.topology_id.context_id.context_uuid.uuid - topology_uuid = request.topology_id.topology_uuid.uuid - with self.session() as session: - topology_add = TopologyModel(topology_uuid=topology_uuid, context_uuid=context_uuid) - updated = True - db_topology = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).one_or_none() - if not db_topology: - updated = False - session.merge(topology_add) - session.commit() - db_topology = session.query(TopologyModel).join(TopologyModel.context).filter(TopologyModel.topology_uuid==topology_uuid).one_or_none() - - for device_id in request.device_ids: - device_uuid = device_id.device_uuid.uuid - td = TopologyDeviceModel(topology_uuid=topology_uuid, device_uuid=device_uuid) - result: Tuple[TopologyDeviceModel, bool] = self.database.create_or_update(td) - - - for link_id in request.link_ids: - link_uuid = link_id.link_uuid.uuid - db_link = session.query(LinkModel).filter( - LinkModel.link_uuid == link_uuid).one_or_none() - tl = TopologyLinkModel(topology_uuid=topology_uuid, link_uuid=link_uuid) - result: Tuple[TopologyDeviceModel, bool] = self.database.create_or_update(tl) - - - - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_topology_id = db_topology.dump_id() - notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) - return TopologyId(**dict_topology_id) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def RemoveTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Empty: - context_uuid = request.context_id.context_uuid.uuid - topology_uuid = request.topology_uuid.uuid - - with self.session() as session: - result = session.query(TopologyModel).filter_by(topology_uuid=topology_uuid, context_uuid=context_uuid).one_or_none() - if not result: - return Empty() - dict_topology_id = result.dump_id() - - session.query(TopologyModel).filter_by(topology_uuid=topology_uuid, context_uuid=context_uuid).delete() - session.commit() - event_type = EventTypeEnum.EVENTTYPE_REMOVE - notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) - return Empty() - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetTopologyEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[TopologyEvent]: - for message in self.messagebroker.consume({TOPIC_TOPOLOGY}, consume_timeout=CONSUME_TIMEOUT): - yield TopologyEvent(**json.loads(message.content)) - - - # ----- Device ----------------------------------------------------------------------------------------------------- - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListDeviceIds(self, request: Empty, context : grpc.ServicerContext) -> DeviceIdList: - with self.session() as session: - result = session.query(DeviceModel).all() - return DeviceIdList(device_ids=[device.dump_id() for device in result]) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListDevices(self, request: Empty, context : grpc.ServicerContext) -> DeviceList: - with self.session() as session: - result = session.query(DeviceModel).all() - return DeviceList(devices=[device.dump() for device in result]) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Device: - device_uuid = request.device_uuid.uuid - with self.session() as session: - result = session.query(DeviceModel).filter(DeviceModel.device_uuid == device_uuid).one_or_none() - if not result: - raise NotFoundException(DeviceModel.__name__.replace('Model', ''), device_uuid) - - rd = result.dump(include_config_rules=True, include_drivers=True, include_endpoints=True) - - rt = Device(**rd) - - return rt - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def SetDevice(self, request: Device, context : grpc.ServicerContext) -> DeviceId: - with self.session() as session: - device_uuid = request.device_id.device_uuid.uuid - - for i, endpoint in enumerate(request.device_endpoints): - endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid - if len(endpoint_device_uuid) == 0: - endpoint_device_uuid = device_uuid - if device_uuid != endpoint_device_uuid: - raise InvalidArgumentException( - 'request.device_endpoints[{:d}].device_id.device_uuid.uuid'.format(i), endpoint_device_uuid, - ['should be == {:s}({:s})'.format('request.device_id.device_uuid.uuid', device_uuid)]) - - config_rules = grpc_config_rules_to_raw(request.device_config.config_rules) - running_config_result = self.update_config(session, device_uuid, 'device', config_rules) - db_running_config = running_config_result[0][0] - config_uuid = db_running_config.config_uuid - running_config_rules = update_config( - self.database, device_uuid, 'device', request.device_config.config_rules) - db_running_config = running_config_rules[0][0] - - new_obj = DeviceModel(**{ - 'device_uuid' : device_uuid, - 'device_type' : request.device_type, - 'device_operational_status' : grpc_to_enum__device_operational_status(request.device_operational_status), - 'device_config_uuid' : config_uuid, - }) - result: Tuple[DeviceModel, bool] = self.database.create_or_update(new_obj) - db_device, updated = result - - self.set_drivers(db_device, request.device_drivers) - - for i, endpoint in enumerate(request.device_endpoints): - endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid - # endpoint_device_uuid = endpoint.endpoint_id.device_id.device_uuid.uuid - # if len(endpoint_device_uuid) == 0: - # endpoint_device_uuid = device_uuid - - endpoint_attributes = { - 'device_uuid' : db_device.device_uuid, - 'endpoint_uuid': endpoint_uuid, - 'endpoint_type': endpoint.endpoint_type, - } - - endpoint_topology_context_uuid = endpoint.endpoint_id.topology_id.context_id.context_uuid.uuid - endpoint_topology_uuid = endpoint.endpoint_id.topology_id.topology_uuid.uuid - if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: - # str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) - - db_topology, topo_dump = self.database.get_object(TopologyModel, endpoint_topology_uuid) - - topology_device = TopologyDeviceModel( - topology_uuid=endpoint_topology_uuid, - device_uuid=db_device.device_uuid) - self.database.create_or_update(topology_device) - - endpoint_attributes['topology_uuid'] = db_topology.topology_uuid - result : Tuple[EndPointModel, bool] = update_or_create_object( - self.database, EndPointModel, str_endpoint_key, endpoint_attributes) - db_endpoint, endpoint_updated = result # pylint: disable=unused-variable - - new_endpoint = EndPointModel(**endpoint_attributes) - result: Tuple[EndPointModel, bool] = self.database.create_or_update(new_endpoint) - db_endpoint, updated = result - - self.set_kpi_sample_types(db_endpoint, endpoint.kpi_sample_types) - - # event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_device_id = db_device.dump_id() - # notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id}) - - return DeviceId(**dict_device_id) - - def set_kpi_sample_types(self, db_endpoint: EndPointModel, grpc_endpoint_kpi_sample_types): - db_endpoint_pk = db_endpoint.endpoint_uuid - for kpi_sample_type in grpc_endpoint_kpi_sample_types: - orm_kpi_sample_type = grpc_to_enum__kpi_sample_type(kpi_sample_type) - # str_endpoint_kpi_sample_type_key = key_to_str([db_endpoint_pk, orm_kpi_sample_type.name]) - data = {'endpoint_uuid': db_endpoint_pk, - 'kpi_sample_type': orm_kpi_sample_type.name, - 'kpi_uuid': str(uuid.uuid4())} - db_endpoint_kpi_sample_type = KpiSampleTypeModel(**data) - self.database.create(db_endpoint_kpi_sample_type) - - def set_drivers(self, db_device: DeviceModel, grpc_device_drivers): - db_device_pk = db_device.device_uuid - for driver in grpc_device_drivers: - orm_driver = grpc_to_enum__device_driver(driver) - str_device_driver_key = key_to_str([db_device_pk, orm_driver.name]) - driver_config = { - # "driver_uuid": str(uuid.uuid4()), - "device_uuid": db_device_pk, - "driver": orm_driver.name - } - db_device_driver = DriverModel(**driver_config) - db_device_driver.device_fk = db_device - db_device_driver.driver = orm_driver - - self.database.create_or_update(db_device_driver) - - def update_config( - self, session, db_parent_pk: str, config_name: str, - raw_config_rules: List[Tuple[ORM_ConfigActionEnum, str, str]] - ) -> List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]]: - - created = False - - db_config = session.query(ConfigModel).filter_by(**{ConfigModel.main_pk_name(): db_parent_pk}).one_or_none() - if not db_config: - db_config = ConfigModel() - setattr(db_config, ConfigModel.main_pk_name(), db_parent_pk) - session.add(db_config) - session.commit() - created = True - - LOGGER.info('UPDATED-CONFIG: {}'.format(db_config.dump())) - - db_objects: List[Tuple[Union[ConfigModel, ConfigRuleModel], bool]] = [(db_config, created)] - - for position, (action, resource_key, resource_value) in enumerate(raw_config_rules): - if action == ORM_ConfigActionEnum.SET: - result : Tuple[ConfigRuleModel, bool] = self.set_config_rule( - db_config, position, resource_key, resource_value) - db_config_rule, updated = result - db_objects.append((db_config_rule, updated)) - elif action == ORM_ConfigActionEnum.DELETE: - self.delete_config_rule(db_config, resource_key) - else: - msg = 'Unsupported action({:s}) for resource_key({:s})/resource_value({:s})' - raise AttributeError( - msg.format(str(ConfigActionEnum.Name(action)), str(resource_key), str(resource_value))) - - return db_objects - - def set_config_rule(self, db_config: ConfigModel, position: int, resource_key: str, resource_value: str, - ): # -> Tuple[ConfigRuleModel, bool]: - - from src.context.service.database.Tools import fast_hasher - str_rule_key_hash = fast_hasher(resource_key) - str_config_rule_key = key_to_str([db_config.config_uuid, str_rule_key_hash], separator=':') - pk = str(uuid.uuid5(uuid.UUID('9566448d-e950-425e-b2ae-7ead656c7e47'), str_config_rule_key)) - data = {'config_rule_uuid': pk, 'config_uuid': db_config.config_uuid, 'position': position, - 'action': ORM_ConfigActionEnum.SET, 'key': resource_key, 'value': resource_value} - to_add = ConfigRuleModel(**data) - - result, updated = self.database.create_or_update(to_add) - return result, updated - - def delete_config_rule( - self, db_config: ConfigModel, resource_key: str - ) -> None: - - from src.context.service.database.Tools import fast_hasher - str_rule_key_hash = fast_hasher(resource_key) - str_config_rule_key = key_to_str([db_config.pk, str_rule_key_hash], separator=':') - - db_config_rule = self.database.get_object(ConfigRuleModel, str_config_rule_key, raise_if_not_found=False) - - if db_config_rule is None: - return - db_config_rule.delete() - - def delete_all_config_rules(self, db_config: ConfigModel) -> None: - - db_config_rule_pks = db_config.references(ConfigRuleModel) - for pk, _ in db_config_rule_pks: ConfigRuleModel(self.database, pk).delete() - - """ - for position, (action, resource_key, resource_value) in enumerate(raw_config_rules): - if action == ORM_ConfigActionEnum.SET: - result: Tuple[ConfigRuleModel, bool] = set_config_rule( - database, db_config, position, resource_key, resource_value) - db_config_rule, updated = result - db_objects.append((db_config_rule, updated)) - elif action == ORM_ConfigActionEnum.DELETE: - delete_config_rule(database, db_config, resource_key) - else: - msg = 'Unsupported action({:s}) for resource_key({:s})/resource_value({:s})' - raise AttributeError( - msg.format(str(ConfigActionEnum.Name(action)), str(resource_key), str(resource_value))) - - return db_objects - """ - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def RemoveDevice(self, request: DeviceId, context : grpc.ServicerContext) -> Empty: - device_uuid = request.device_uuid.uuid - - with self.session() as session: - db_device = session.query(DeviceModel).filter_by(device_uuid=device_uuid).one_or_none() - - session.query(TopologyDeviceModel).filter_by(device_uuid=device_uuid).delete() - session.query(ConfigRuleModel).filter_by(config_uuid=db_device.device_config_uuid).delete() - session.query(ConfigModel).filter_by(config_uuid=db_device.device_config_uuid).delete() - - if not db_device: - return Empty() - dict_device_id = db_device.dump_id() - - session.query(DeviceModel).filter_by(device_uuid=device_uuid).delete() - session.commit() - event_type = EventTypeEnum.EVENTTYPE_REMOVE - notify_event(self.messagebroker, TOPIC_DEVICE, event_type, {'device_id': dict_device_id}) - return Empty() - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetDeviceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[DeviceEvent]: - for message in self.messagebroker.consume({TOPIC_DEVICE}, consume_timeout=CONSUME_TIMEOUT): - yield DeviceEvent(**json.loads(message.content)) - - - - - # ----- Link ------------------------------------------------------------------------------------------------------- - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListLinkIds(self, request: Empty, context : grpc.ServicerContext) -> LinkIdList: - with self.session() as session: - result = session.query(LinkModel).all() - return LinkIdList(link_ids=[db_link.dump_id() for db_link in result]) - - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListLinks(self, request: Empty, context : grpc.ServicerContext) -> LinkList: - with self.session() as session: - link_list = LinkList() - - db_links = session.query(LinkModel).all() - - for db_link in db_links: - link_uuid = db_link.link_uuid - filt = {'link_uuid': link_uuid} - link_endpoints = session.query(LinkEndPointModel).filter_by(**filt).all() - if link_endpoints: - eps = [] - for lep in link_endpoints: - filt = {'endpoint_uuid': lep.endpoint_uuid} - eps.append(session.query(EndPointModel).filter_by(**filt).one()) - link_list.links.append(Link(**db_link.dump(eps))) - - return link_list - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetLink(self, request: LinkId, context : grpc.ServicerContext) -> Link: - link_uuid = request.link_uuid.uuid - with self.session() as session: - result = session.query(LinkModel).filter(LinkModel.link_uuid == link_uuid).one_or_none() - if not result: - raise NotFoundException(LinkModel.__name__.replace('Model', ''), link_uuid) - - filt = {'link_uuid': link_uuid} - link_endpoints = session.query(LinkEndPointModel).filter_by(**filt).all() - if link_endpoints: - eps = [] - for lep in link_endpoints: - filt = {'endpoint_uuid': lep.endpoint_uuid} - eps.append(session.query(EndPointModel).filter_by(**filt).one()) - return Link(**result.dump(eps)) - - rd = result.dump() - rt = Link(**rd) - - return rt - - - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def SetLink(self, request: Link, context : grpc.ServicerContext) -> LinkId: - link_uuid = request.link_id.link_uuid.uuid - - new_link = LinkModel(**{ - 'link_uuid': link_uuid - }) - result: Tuple[LinkModel, bool] = self.database.create_or_update(new_link) - db_link, updated = result - - for endpoint_id in request.link_endpoint_ids: - endpoint_uuid = endpoint_id.endpoint_uuid.uuid - endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid - endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid - endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid - - - db_topology = None - if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: - db_topology: TopologyModel = self.database.get_object(TopologyModel, endpoint_topology_uuid) - # check device is in topology - self.database.get_object(TopologyDeviceModel, endpoint_device_uuid) - - - link_endpoint = LinkEndPointModel(link_uuid=link_uuid, endpoint_uuid=endpoint_uuid) - result: Tuple[LinkEndPointModel, bool] = self.database.create_or_update(link_endpoint) - - if db_topology is not None: - topology_link = TopologyLinkModel(topology_uuid=endpoint_topology_uuid, link_uuid=link_uuid) - result: Tuple[TopologyLinkModel, bool] = self.database.create_or_update(topology_link) - - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_link_id = db_link.dump_id() - notify_event(self.messagebroker, TOPIC_LINK, event_type, {'link_id': dict_link_id}) - return LinkId(**dict_link_id) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def RemoveLink(self, request: LinkId, context : grpc.ServicerContext) -> Empty: - with self.session() as session: - link_uuid = request.link_uuid.uuid - - session.query(TopologyLinkModel).filter_by(link_uuid=link_uuid).delete() - session.query(LinkEndPointModel).filter_by(link_uuid=link_uuid).delete() - - result = session.query(LinkModel).filter_by(link_uuid=link_uuid).one_or_none() - if not result: - return Empty() - dict_link_id = result.dump_id() - - session.query(LinkModel).filter_by(link_uuid=link_uuid).delete() - session.commit() - event_type = EventTypeEnum.EVENTTYPE_REMOVE - notify_event(self.messagebroker, TOPIC_LINK, event_type, {'link_id': dict_link_id}) - return Empty() - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetLinkEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[LinkEvent]: - for message in self.messagebroker.consume({TOPIC_LINK}, consume_timeout=CONSUME_TIMEOUT): - yield LinkEvent(**json.loads(message.content)) - - - # ----- Service ---------------------------------------------------------------------------------------------------- - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListServiceIds(self, request: ContextId, context : grpc.ServicerContext) -> ServiceIdList: - context_uuid = request.context_uuid.uuid - - with self.session() as session: - db_services = session.query(ServiceModel).filter_by(context_uuid=context_uuid).all() - return ServiceIdList(service_ids=[db_service.dump_id() for db_service in db_services]) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListServices(self, request: ContextId, context : grpc.ServicerContext) -> ServiceList: - context_uuid = request.context_uuid.uuid - - with self.session() as session: - db_services = session.query(ServiceModel).filter_by(context_uuid=context_uuid).all() - return ServiceList(services=[db_service.dump() for db_service in db_services]) - - - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetService(self, request: ServiceId, context : grpc.ServicerContext) -> Service: - service_uuid = request.service_uuid.uuid - with self.session() as session: - result = session.query(ServiceModel).filter_by(service_uuid=service_uuid).one_or_none() - - if not result: - raise NotFoundException(ServiceModel.__name__.replace('Model', ''), service_uuid) - - return Service(**result.dump()) - - def set_constraint(self, db_constraints: ConstraintsModel, grpc_constraint: Constraint, position: int - ) -> Tuple[Union_ConstraintModel, bool]: - with self.session() as session: - - grpc_constraint_kind = str(grpc_constraint.WhichOneof('constraint')) - - parser = CONSTRAINT_PARSERS.get(grpc_constraint_kind) - if parser is None: - raise NotImplementedError('Constraint of kind {:s} is not implemented: {:s}'.format( - grpc_constraint_kind, grpc_message_to_json_string(grpc_constraint))) - - # create specific constraint - constraint_class, str_constraint_id, constraint_data, constraint_kind = parser(grpc_constraint) - str_constraint_id = str(uuid.uuid4()) - LOGGER.info('str_constraint_id: {}'.format(str_constraint_id)) - # str_constraint_key_hash = fast_hasher(':'.join([constraint_kind.value, str_constraint_id])) - # str_constraint_key = key_to_str([db_constraints.pk, str_constraint_key_hash], separator=':') - - # result : Tuple[Union_ConstraintModel, bool] = update_or_create_object( - # database, constraint_class, str_constraint_key, constraint_data) - constraint_data[constraint_class.main_pk_name()] = str_constraint_id - db_new_constraint = constraint_class(**constraint_data) - result: Tuple[Union_ConstraintModel, bool] = self.database.create_or_update(db_new_constraint) - db_specific_constraint, updated = result - - # create generic constraint - # constraint_fk_field_name = 'constraint_uuid'.format(constraint_kind.value) - constraint_data = { - 'constraints_uuid': db_constraints.constraints_uuid, 'position': position, 'kind': constraint_kind - } - - db_new_constraint = ConstraintModel(**constraint_data) - result: Tuple[Union_ConstraintModel, bool] = self.database.create_or_update(db_new_constraint) - db_constraint, updated = result - - return db_constraint, updated - - def set_constraints(self, service_uuid: str, constraints_name : str, grpc_constraints - ) -> List[Tuple[Union[ConstraintsModel, ConstraintModel], bool]]: - with self.session() as session: - # str_constraints_key = key_to_str([db_parent_pk, constraints_name], separator=':') - # result : Tuple[ConstraintsModel, bool] = get_or_create_object(database, ConstraintsModel, str_constraints_key) - result = session.query(ConstraintsModel).filter_by(constraints_uuid=service_uuid).one_or_none() - created = None - if result: - created = True - session.query(ConstraintsModel).filter_by(constraints_uuid=service_uuid).one_or_none() - db_constraints = ConstraintsModel(constraints_uuid=service_uuid) - session.add(db_constraints) - - db_objects = [(db_constraints, created)] - - for position,grpc_constraint in enumerate(grpc_constraints): - result : Tuple[ConstraintModel, bool] = self.set_constraint( - db_constraints, grpc_constraint, position) - db_constraint, updated = result - db_objects.append((db_constraint, updated)) - - return db_objects - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def SetService(self, request: Service, context : grpc.ServicerContext) -> ServiceId: - with self.lock: - with self.session() as session: - - context_uuid = request.service_id.context_id.context_uuid.uuid - # db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) - db_context = session.query(ContextModel).filter_by(context_uuid=context_uuid).one_or_none() - - for i,endpoint_id in enumerate(request.service_endpoint_ids): - endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid - if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: - raise InvalidArgumentException( - 'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), - endpoint_topology_context_uuid, - ['should be == {:s}({:s})'.format( - 'request.service_id.context_id.context_uuid.uuid', context_uuid)]) - - service_uuid = request.service_id.service_uuid.uuid - # str_service_key = key_to_str([context_uuid, service_uuid]) - - constraints_result = self.set_constraints(service_uuid, 'constraints', request.service_constraints) - db_constraints = constraints_result[0][0] - - config_rules = grpc_config_rules_to_raw(request.service_config.config_rules) - running_config_result = update_config(self.database, str_service_key, 'running', config_rules) - db_running_config = running_config_result[0][0] - - result : Tuple[ServiceModel, bool] = update_or_create_object(self.database, ServiceModel, str_service_key, { - 'context_fk' : db_context, - 'service_uuid' : service_uuid, - 'service_type' : grpc_to_enum__service_type(request.service_type), - 'service_constraints_fk': db_constraints, - 'service_status' : grpc_to_enum__service_status(request.service_status.service_status), - 'service_config_fk' : db_running_config, - }) - db_service, updated = result - - for i,endpoint_id in enumerate(request.service_endpoint_ids): - endpoint_uuid = endpoint_id.endpoint_uuid.uuid - endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid - endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid - endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid - - str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) - if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: - str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) - str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') - - db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) - - str_service_endpoint_key = key_to_str([service_uuid, str_endpoint_key], separator='--') - result : Tuple[ServiceEndPointModel, bool] = get_or_create_object( - self.database, ServiceEndPointModel, str_service_endpoint_key, { - 'service_fk': db_service, 'endpoint_fk': db_endpoint}) - #db_service_endpoint, service_endpoint_created = result - - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_service_id = db_service.dump_id() - notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id}) - return ServiceId(**dict_service_id) - context_uuid = request.service_id.context_id.context_uuid.uuid - db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) - - for i,endpoint_id in enumerate(request.service_endpoint_ids): - endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid - if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: - raise InvalidArgumentException( - 'request.service_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), - endpoint_topology_context_uuid, - ['should be == {:s}({:s})'.format( - 'request.service_id.context_id.context_uuid.uuid', context_uuid)]) - - service_uuid = request.service_id.service_uuid.uuid - str_service_key = key_to_str([context_uuid, service_uuid]) - - constraints_result = set_constraints( - self.database, str_service_key, 'service', request.service_constraints) - db_constraints = constraints_result[0][0] - - running_config_rules = update_config( - self.database, str_service_key, 'service', request.service_config.config_rules) - db_running_config = running_config_rules[0][0] - - result : Tuple[ServiceModel, bool] = update_or_create_object(self.database, ServiceModel, str_service_key, { - 'context_fk' : db_context, - 'service_uuid' : service_uuid, - 'service_type' : grpc_to_enum__service_type(request.service_type), - 'service_constraints_fk': db_constraints, - 'service_status' : grpc_to_enum__service_status(request.service_status.service_status), - 'service_config_fk' : db_running_config, - }) - db_service, updated = result - - for i,endpoint_id in enumerate(request.service_endpoint_ids): - endpoint_uuid = endpoint_id.endpoint_uuid.uuid - endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid - endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid - endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid - - str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) - if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: - str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) - str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') - - db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) - - str_service_endpoint_key = key_to_str([service_uuid, str_endpoint_key], separator='--') - result : Tuple[ServiceEndPointModel, bool] = get_or_create_object( - self.database, ServiceEndPointModel, str_service_endpoint_key, { - 'service_fk': db_service, 'endpoint_fk': db_endpoint}) - #db_service_endpoint, service_endpoint_created = result - - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_service_id = db_service.dump_id() - notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id}) - return ServiceId(**dict_service_id) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def RemoveService(self, request: ServiceId, context : grpc.ServicerContext) -> Empty: - with self.lock: - context_uuid = request.context_id.context_uuid.uuid - service_uuid = request.service_uuid.uuid - db_service = ServiceModel(self.database, key_to_str([context_uuid, service_uuid]), auto_load=False) - found = db_service.load() - if not found: return Empty() - - dict_service_id = db_service.dump_id() - db_service.delete() - - event_type = EventTypeEnum.EVENTTYPE_REMOVE - notify_event(self.messagebroker, TOPIC_SERVICE, event_type, {'service_id': dict_service_id}) - return Empty() - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetServiceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ServiceEvent]: - for message in self.messagebroker.consume({TOPIC_SERVICE}, consume_timeout=CONSUME_TIMEOUT): - yield ServiceEvent(**json.loads(message.content)) - - - # ----- Slice ---------------------------------------------------------------------------------------------------- - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListSliceIds(self, request: ContextId, context : grpc.ServicerContext) -> SliceIdList: - with self.lock: - db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid) - db_slices : Set[SliceModel] = get_related_objects(db_context, SliceModel) - db_slices = sorted(db_slices, key=operator.attrgetter('pk')) - return SliceIdList(slice_ids=[db_slice.dump_id() for db_slice in db_slices]) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListSlices(self, request: ContextId, context : grpc.ServicerContext) -> SliceList: - with self.lock: - db_context : ContextModel = get_object(self.database, ContextModel, request.context_uuid.uuid) - db_slices : Set[SliceModel] = get_related_objects(db_context, SliceModel) - db_slices = sorted(db_slices, key=operator.attrgetter('pk')) - return SliceList(slices=[db_slice.dump() for db_slice in db_slices]) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetSlice(self, request: SliceId, context : grpc.ServicerContext) -> Slice: - with self.lock: - str_key = key_to_str([request.context_id.context_uuid.uuid, request.slice_uuid.uuid]) - db_slice : SliceModel = get_object(self.database, SliceModel, str_key) - return Slice(**db_slice.dump( - include_endpoint_ids=True, include_constraints=True, include_config_rules=True, - include_service_ids=True, include_subslice_ids=True)) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def SetSlice(self, request: Slice, context : grpc.ServicerContext) -> SliceId: - with self.lock: - context_uuid = request.slice_id.context_id.context_uuid.uuid - db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) - - for i,endpoint_id in enumerate(request.slice_endpoint_ids): - endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid - if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: - raise InvalidArgumentException( - 'request.slice_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), - endpoint_topology_context_uuid, - ['should be == {:s}({:s})'.format( - 'request.slice_id.context_id.context_uuid.uuid', context_uuid)]) - - slice_uuid = request.slice_id.slice_uuid.uuid - str_slice_key = key_to_str([context_uuid, slice_uuid]) - - constraints_result = set_constraints( - self.database, str_slice_key, 'slice', request.slice_constraints) - db_constraints = constraints_result[0][0] - - running_config_rules = update_config( - self.database, str_slice_key, 'slice', request.slice_config.config_rules) - db_running_config = running_config_rules[0][0] - - result : Tuple[SliceModel, bool] = update_or_create_object(self.database, SliceModel, str_slice_key, { - 'context_fk' : db_context, - 'slice_uuid' : slice_uuid, - 'slice_constraints_fk': db_constraints, - 'slice_status' : grpc_to_enum__slice_status(request.slice_status.slice_status), - 'slice_config_fk' : db_running_config, - 'slice_owner_uuid' : request.slice_owner.owner_uuid.uuid, - 'slice_owner_string' : request.slice_owner.owner_string, - }) - db_slice, updated = result - - for i,endpoint_id in enumerate(request.slice_endpoint_ids): - endpoint_uuid = endpoint_id.endpoint_uuid.uuid - endpoint_device_uuid = endpoint_id.device_id.device_uuid.uuid - endpoint_topology_uuid = endpoint_id.topology_id.topology_uuid.uuid - endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid - - str_endpoint_key = key_to_str([endpoint_device_uuid, endpoint_uuid]) - if len(endpoint_topology_context_uuid) > 0 and len(endpoint_topology_uuid) > 0: - str_topology_key = key_to_str([endpoint_topology_context_uuid, endpoint_topology_uuid]) - str_endpoint_key = key_to_str([str_endpoint_key, str_topology_key], separator=':') - - db_endpoint : EndPointModel = get_object(self.database, EndPointModel, str_endpoint_key) - - str_slice_endpoint_key = key_to_str([str_slice_key, str_endpoint_key], separator='--') - result : Tuple[SliceEndPointModel, bool] = get_or_create_object( - self.database, SliceEndPointModel, str_slice_endpoint_key, { - 'slice_fk': db_slice, 'endpoint_fk': db_endpoint}) - #db_slice_endpoint, slice_endpoint_created = result - - for i,service_id in enumerate(request.slice_service_ids): - service_uuid = service_id.service_uuid.uuid - service_context_uuid = service_id.context_id.context_uuid.uuid - str_service_key = key_to_str([service_context_uuid, service_uuid]) - db_service : ServiceModel = get_object(self.database, ServiceModel, str_service_key) - - str_slice_service_key = key_to_str([str_slice_key, str_service_key], separator='--') - result : Tuple[SliceServiceModel, bool] = get_or_create_object( - self.database, SliceServiceModel, str_slice_service_key, { - 'slice_fk': db_slice, 'service_fk': db_service}) - #db_slice_service, slice_service_created = result - - for i,subslice_id in enumerate(request.slice_subslice_ids): - subslice_uuid = subslice_id.slice_uuid.uuid - subslice_context_uuid = subslice_id.context_id.context_uuid.uuid - str_subslice_key = key_to_str([subslice_context_uuid, subslice_uuid]) - db_subslice : SliceModel = get_object(self.database, SliceModel, str_subslice_key) - - str_slice_subslice_key = key_to_str([str_slice_key, str_subslice_key], separator='--') - result : Tuple[SliceSubSliceModel, bool] = get_or_create_object( - self.database, SliceSubSliceModel, str_slice_subslice_key, { - 'slice_fk': db_slice, 'sub_slice_fk': db_subslice}) - #db_slice_subslice, slice_subslice_created = result - - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_slice_id = db_slice.dump_id() - notify_event(self.messagebroker, TOPIC_SLICE, event_type, {'slice_id': dict_slice_id}) - return SliceId(**dict_slice_id) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def UnsetSlice(self, request: Slice, context : grpc.ServicerContext) -> SliceId: - with self.lock: - context_uuid = request.slice_id.context_id.context_uuid.uuid - db_context : ContextModel = get_object(self.database, ContextModel, context_uuid) - - for i,endpoint_id in enumerate(request.slice_endpoint_ids): - endpoint_topology_context_uuid = endpoint_id.topology_id.context_id.context_uuid.uuid - if len(endpoint_topology_context_uuid) > 0 and context_uuid != endpoint_topology_context_uuid: - raise InvalidArgumentException( - 'request.slice_endpoint_ids[{:d}].topology_id.context_id.context_uuid.uuid'.format(i), - endpoint_topology_context_uuid, - ['should be == {:s}({:s})'.format( - 'request.slice_id.context_id.context_uuid.uuid', context_uuid)]) - - slice_uuid = request.slice_id.slice_uuid.uuid - str_slice_key = key_to_str([context_uuid, slice_uuid]) - - if len(request.slice_constraints) > 0: - raise NotImplementedError('UnsetSlice: removal of constraints') - if len(request.slice_config.config_rules) > 0: - raise NotImplementedError('UnsetSlice: removal of config rules') - if len(request.slice_endpoint_ids) > 0: - raise NotImplementedError('UnsetSlice: removal of endpoints') - - updated = False - - for service_id in request.slice_service_ids: - service_uuid = service_id.service_uuid.uuid - service_context_uuid = service_id.context_id.context_uuid.uuid - str_service_key = key_to_str([service_context_uuid, service_uuid]) - str_slice_service_key = key_to_str([str_slice_key, str_service_key], separator='--') - SliceServiceModel(self.database, str_slice_service_key).delete() - updated = True - - for subslice_id in request.slice_subslice_ids: - subslice_uuid = subslice_id.slice_uuid.uuid - subslice_context_uuid = subslice_id.context_id.context_uuid.uuid - str_subslice_key = key_to_str([subslice_context_uuid, subslice_uuid]) - str_slice_subslice_key = key_to_str([str_slice_key, str_subslice_key], separator='--') - SliceSubSliceModel(self.database, str_slice_subslice_key).delete() - updated = True - - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - db_slice : SliceModel = get_object(self.database, SliceModel, str_slice_key) - dict_slice_id = db_slice.dump_id() - notify_event(self.messagebroker, TOPIC_SLICE, event_type, {'slice_id': dict_slice_id}) - return SliceId(**dict_slice_id) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def RemoveSlice(self, request: SliceId, context : grpc.ServicerContext) -> Empty: - with self.lock: - context_uuid = request.context_id.context_uuid.uuid - slice_uuid = request.slice_uuid.uuid - db_slice = SliceModel(self.database, key_to_str([context_uuid, slice_uuid]), auto_load=False) - found = db_slice.load() - if not found: return Empty() - - dict_slice_id = db_slice.dump_id() - db_slice.delete() - - event_type = EventTypeEnum.EVENTTYPE_REMOVE - notify_event(self.messagebroker, TOPIC_SLICE, event_type, {'slice_id': dict_slice_id}) - return Empty() - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetSliceEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[SliceEvent]: - for message in self.messagebroker.consume({TOPIC_SLICE}, consume_timeout=CONSUME_TIMEOUT): - yield SliceEvent(**json.loads(message.content)) - - - # ----- Connection ------------------------------------------------------------------------------------------------- - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListConnectionIds(self, request: ServiceId, context : grpc.ServicerContext) -> ConnectionIdList: - with self.session() as session: - result = session.query(DeviceModel).all() - return DeviceIdList(device_ids=[device.dump_id() for device in result]) - - with self.lock: - str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid]) - db_service : ServiceModel = get_object(self.database, ServiceModel, str_key) - db_connections : Set[ConnectionModel] = get_related_objects(db_service, ConnectionModel) - db_connections = sorted(db_connections, key=operator.attrgetter('pk')) - return ConnectionIdList(connection_ids=[db_connection.dump_id() for db_connection in db_connections]) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListConnections(self, request: ContextId, context : grpc.ServicerContext) -> ServiceList: - with self.lock: - str_key = key_to_str([request.context_id.context_uuid.uuid, request.service_uuid.uuid]) - db_service : ServiceModel = get_object(self.database, ServiceModel, str_key) - db_connections : Set[ConnectionModel] = get_related_objects(db_service, ConnectionModel) - db_connections = sorted(db_connections, key=operator.attrgetter('pk')) - return ConnectionList(connections=[db_connection.dump() for db_connection in db_connections]) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetConnection(self, request: ConnectionId, context : grpc.ServicerContext) -> Connection: - with self.lock: - db_connection : ConnectionModel = get_object(self.database, ConnectionModel, request.connection_uuid.uuid) - return Connection(**db_connection.dump(include_path=True, include_sub_service_ids=True)) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def SetConnection(self, request: Connection, context : grpc.ServicerContext) -> ConnectionId: - with self.lock: - connection_uuid = request.connection_id.connection_uuid.uuid - - connection_attributes = {'connection_uuid': connection_uuid} - - service_context_uuid = request.service_id.context_id.context_uuid.uuid - service_uuid = request.service_id.service_uuid.uuid - if len(service_context_uuid) > 0 and len(service_uuid) > 0: - str_service_key = key_to_str([service_context_uuid, service_uuid]) - db_service : ServiceModel = get_object(self.database, ServiceModel, str_service_key) - connection_attributes['service_fk'] = db_service - - path_hops_result = set_path(self.database, connection_uuid, request.path_hops_endpoint_ids, path_name = '') - db_path = path_hops_result[0] - connection_attributes['path_fk'] = db_path - - result : Tuple[ConnectionModel, bool] = update_or_create_object( - self.database, ConnectionModel, connection_uuid, connection_attributes) - db_connection, updated = result - - for sub_service_id in request.sub_service_ids: - sub_service_uuid = sub_service_id.service_uuid.uuid - sub_service_context_uuid = sub_service_id.context_id.context_uuid.uuid - str_sub_service_key = key_to_str([sub_service_context_uuid, sub_service_uuid]) - db_service : ServiceModel = get_object(self.database, ServiceModel, str_sub_service_key) - - str_connection_sub_service_key = key_to_str([connection_uuid, str_sub_service_key], separator='--') - result : Tuple[ConnectionSubServiceModel, bool] = get_or_create_object( - self.database, ConnectionSubServiceModel, str_connection_sub_service_key, { - 'connection_fk': db_connection, 'sub_service_fk': db_service}) - #db_connection_sub_service, connection_sub_service_created = result - - event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_connection_id = db_connection.dump_id() - notify_event(self.messagebroker, TOPIC_CONNECTION, event_type, {'connection_id': dict_connection_id}) - return ConnectionId(**dict_connection_id) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def RemoveConnection(self, request: ConnectionId, context : grpc.ServicerContext) -> Empty: - with self.lock: - db_connection = ConnectionModel(self.database, request.connection_uuid.uuid, auto_load=False) - found = db_connection.load() - if not found: return Empty() - - dict_connection_id = db_connection.dump_id() - db_connection.delete() - - event_type = EventTypeEnum.EVENTTYPE_REMOVE - notify_event(self.messagebroker, TOPIC_CONNECTION, event_type, {'connection_id': dict_connection_id}) - return Empty() - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetConnectionEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ConnectionEvent]: - for message in self.messagebroker.consume({TOPIC_CONNECTION}, consume_timeout=CONSUME_TIMEOUT): - yield ConnectionEvent(**json.loads(message.content)) - - - # ----- Policy ----------------------------------------------------------------------------------------------------- - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListPolicyRuleIds(self, request: Empty, context: grpc.ServicerContext) -> PolicyRuleIdList: - with self.lock: - db_policy_rules: List[PolicyRuleModel] = get_all_objects(self.database, PolicyRuleModel) - db_policy_rules = sorted(db_policy_rules, key=operator.attrgetter('pk')) - return PolicyRuleIdList(policyRuleIdList=[db_policy_rule.dump_id() for db_policy_rule in db_policy_rules]) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListPolicyRules(self, request: Empty, context: grpc.ServicerContext) -> PolicyRuleList: - with self.lock: - db_policy_rules: List[PolicyRuleModel] = get_all_objects(self.database, PolicyRuleModel) - db_policy_rules = sorted(db_policy_rules, key=operator.attrgetter('pk')) - return PolicyRuleList(policyRules=[db_policy_rule.dump() for db_policy_rule in db_policy_rules]) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def GetPolicyRule(self, request: PolicyRuleId, context: grpc.ServicerContext) -> PolicyRule: - with self.lock: - policy_rule_uuid = request.uuid.uuid - db_policy_rule: PolicyRuleModel = get_object(self.database, PolicyRuleModel, policy_rule_uuid) - return PolicyRule(**db_policy_rule.dump()) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def SetPolicyRule(self, request: PolicyRule, context: grpc.ServicerContext) -> PolicyRuleId: - with self.lock: - policy_rule_type = request.WhichOneof('policy_rule') - policy_rule_json = grpc_message_to_json(request) - policy_rule_uuid = policy_rule_json[policy_rule_type]['policyRuleBasic']['policyRuleId']['uuid']['uuid'] - result: Tuple[PolicyRuleModel, bool] = update_or_create_object( - self.database, PolicyRuleModel, policy_rule_uuid, {'value': json.dumps(policy_rule_json)}) - db_policy, updated = result # pylint: disable=unused-variable - - #event_type = EventTypeEnum.EVENTTYPE_UPDATE if updated else EventTypeEnum.EVENTTYPE_CREATE - dict_policy_id = db_policy.dump_id() - #notify_event(self.messagebroker, TOPIC_POLICY, event_type, {"policy_id": dict_policy_id}) - return PolicyRuleId(**dict_policy_id) - - @safe_and_metered_rpc_method(METRICS, LOGGER) - def RemovePolicyRule(self, request: PolicyRuleId, context: grpc.ServicerContext) -> Empty: - with self.lock: - policy_uuid = request.uuid.uuid - db_policy = PolicyRuleModel(self.database, policy_uuid, auto_load=False) - found = db_policy.load() - if not found: return Empty() - - dict_policy_id = db_policy.dump_id() - db_policy.delete() - #event_type = EventTypeEnum.EVENTTYPE_REMOVE - #notify_event(self.messagebroker, TOPIC_POLICY, event_type, {"policy_id": dict_policy_id}) - return Empty() diff --git a/src/context/tests/test_unitary.py b/src/context/tests/test_unitary.py index 67dd64fb3..aaa8c7fbd 100644 --- a/src/context/tests/test_unitary.py +++ b/src/context/tests/test_unitary.py @@ -37,13 +37,13 @@ from context.client.EventsCollector import EventsCollector from context.service.database.Tools import ( FASTHASHER_DATA_ACCEPTED_FORMAT, FASTHASHER_ITEM_ACCEPTED_FORMAT, fast_hasher) from context.service.grpc_server.ContextService import ContextService -from context.service.Populate import populate +from context.service._old_code.Populate import populate from context.service.rest_server.RestServer import RestServer from context.service.rest_server.Resources import RESOURCES from requests import Session from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from context.service.database.Base import Base +from context.service.database._Base import Base from .Objects import ( CONNECTION_R1_R3, CONNECTION_R1_R3_ID, CONNECTION_R1_R3_UUID, CONTEXT, CONTEXT_ID, DEVICE_R1, DEVICE_R1_ID, @@ -1294,134 +1294,6 @@ def test_grpc_policy( -# ----- Test REST API methods ------------------------------------------------------------------------------------------ - -def test_rest_populate_database( - context_db_mb : Tuple[Database, MessageBroker], # pylint: disable=redefined-outer-name - context_service_grpc : ContextService # pylint: disable=redefined-outer-name - ): - database = context_db_mb[0] - database.clear_all() - populate(LOCAL_HOST, GRPC_PORT) - -def test_rest_get_context_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - reply = do_rest_request('/context_ids') - validate_context_ids(reply) - -def test_rest_get_contexts(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - reply = do_rest_request('/contexts') - validate_contexts(reply) - -def test_rest_get_context(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) - reply = do_rest_request('/context/{:s}'.format(context_uuid)) - validate_context(reply) - -def test_rest_get_topology_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) - reply = do_rest_request('/context/{:s}/topology_ids'.format(context_uuid)) - validate_topology_ids(reply) - -def test_rest_get_topologies(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) - reply = do_rest_request('/context/{:s}/topologies'.format(context_uuid)) - validate_topologies(reply) - -def test_rest_get_topology(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) - topology_uuid = urllib.parse.quote(DEFAULT_TOPOLOGY_UUID) - reply = do_rest_request('/context/{:s}/topology/{:s}'.format(context_uuid, topology_uuid)) - validate_topology(reply, num_devices=3, num_links=3) - -def test_rest_get_service_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) - reply = do_rest_request('/context/{:s}/service_ids'.format(context_uuid)) - validate_service_ids(reply) - -def test_rest_get_services(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) - reply = do_rest_request('/context/{:s}/services'.format(context_uuid)) - validate_services(reply) - -def test_rest_get_service(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) - service_uuid = urllib.parse.quote(SERVICE_R1_R2_UUID, safe='') - reply = do_rest_request('/context/{:s}/service/{:s}'.format(context_uuid, service_uuid)) - validate_service(reply) - -def test_rest_get_slice_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) - reply = do_rest_request('/context/{:s}/slice_ids'.format(context_uuid)) - #validate_slice_ids(reply) - -def test_rest_get_slices(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) - reply = do_rest_request('/context/{:s}/slices'.format(context_uuid)) - #validate_slices(reply) - -#def test_rest_get_slice(context_service_rest : RestServer): # pylint: disable=redefined-outer-name -# context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) -# slice_uuid = urllib.parse.quote(SLICE_R1_R2_UUID, safe='') -# reply = do_rest_request('/context/{:s}/slice/{:s}'.format(context_uuid, slice_uuid)) -# #validate_slice(reply) - -def test_rest_get_device_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - reply = do_rest_request('/device_ids') - validate_device_ids(reply) - -def test_rest_get_devices(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - reply = do_rest_request('/devices') - validate_devices(reply) - -def test_rest_get_device(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - device_uuid = urllib.parse.quote(DEVICE_R1_UUID, safe='') - reply = do_rest_request('/device/{:s}'.format(device_uuid)) - validate_device(reply) - -def test_rest_get_link_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - reply = do_rest_request('/link_ids') - validate_link_ids(reply) - -def test_rest_get_links(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - reply = do_rest_request('/links') - validate_links(reply) - -def test_rest_get_link(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - link_uuid = urllib.parse.quote(LINK_R1_R2_UUID, safe='') - reply = do_rest_request('/link/{:s}'.format(link_uuid)) - validate_link(reply) - -def test_rest_get_connection_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) - service_uuid = urllib.parse.quote(SERVICE_R1_R3_UUID, safe='') - reply = do_rest_request('/context/{:s}/service/{:s}/connection_ids'.format(context_uuid, service_uuid)) - validate_connection_ids(reply) - -def test_rest_get_connections(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - context_uuid = urllib.parse.quote(DEFAULT_CONTEXT_UUID) - service_uuid = urllib.parse.quote(SERVICE_R1_R3_UUID, safe='') - reply = do_rest_request('/context/{:s}/service/{:s}/connections'.format(context_uuid, service_uuid)) - validate_connections(reply) - -def test_rest_get_connection(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - connection_uuid = urllib.parse.quote(CONNECTION_R1_R3_UUID, safe='') - reply = do_rest_request('/connection/{:s}'.format(connection_uuid)) - validate_connection(reply) - -def test_rest_get_policyrule_ids(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - reply = do_rest_request('/policyrule_ids') - #validate_policyrule_ids(reply) - -def test_rest_get_policyrules(context_service_rest : RestServer): # pylint: disable=redefined-outer-name - reply = do_rest_request('/policyrules') - #validate_policyrules(reply) - -#def test_rest_get_policyrule(context_service_rest : RestServer): # pylint: disable=redefined-outer-name -# policyrule_uuid = urllib.parse.quote(POLICYRULE_UUID, safe='') -# reply = do_rest_request('/policyrule/{:s}'.format(policyrule_uuid)) -# #validate_policyrule(reply) - - # ----- Test misc. Context internal tools ------------------------------------------------------------------------------ def test_tools_fast_string_hasher(): -- GitLab From d3aa64a2bed396e0b71f678bce30bed148384fbb Mon Sep 17 00:00:00 2001 From: longllu Date: Thu, 15 Dec 2022 10:10:35 +0000 Subject: [PATCH 082/353] WebUI: - Update device form. --- src/webui/service/device/forms.py | 2 +- src/webui/service/device/routes.py | 43 ++++++++----------- .../service/templates/device/update.html | 11 ++--- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/src/webui/service/device/forms.py b/src/webui/service/device/forms.py index 3aae96cc0..cfa741ab3 100644 --- a/src/webui/service/device/forms.py +++ b/src/webui/service/device/forms.py @@ -50,7 +50,7 @@ class ConfigForm(FlaskForm): class UpdateDeviceForm(FlaskForm): - config_operational_status = SelectField('Operational Status', + update_operational_status = SelectField('Operational Status', choices=[(-1, 'Select...'), (0, 'Undefined'), (1, 'Disabled'), (2, 'Enabled')], coerce=int, validators=[NumberRange(min=0)]) diff --git a/src/webui/service/device/routes.py b/src/webui/service/device/routes.py index 105fe3b98..fe475594b 100644 --- a/src/webui/service/device/routes.py +++ b/src/webui/service/device/routes.py @@ -207,28 +207,23 @@ def update(device_uuid): response = context_client.GetDevice(request) context_client.close() - ## listing enum values - #form.operational_status.choices = [] - #for key, value in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_name.items(): - # form.operational_status.choices.append((DeviceOperationalStatusEnum.Value(key), key.replace('DEVICEOPERATIONALSTATUS_', ''))) -# - #form.operational_status.default = response.device_operational_status - print(form.errors) - if form.is_submitted(): - print("submitted") - if form.validate(): - print("valid") - print(form.errors) - #if form.validate_on_submit(): - # device = Device() - # device.CopyFrom(response) - # device.device_operational_status = form.operational_status.data - # try: - # device_client.connect() - # response: DeviceId = device_client.ConfigureDevice(device) - # device_client.close() - # flash(f'New configuration was created with ID "{response.device_uuid.uuid}".', 'success') - # return redirect(url_for('device.home')) - # except Exception as e: - # flash(f'Problem adding the device. {e.details()}', 'danger') + # listing enum values + form.update_operational_status.choices = [] + for key, value in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_name.items(): + form.update_operational_status.choices.append((DeviceOperationalStatusEnum.Value(key), key.replace('DEVICEOPERATIONALSTATUS_', ''))) + + form.update_operational_status.default = response.device_operational_status + + if form.validate_on_submit(): + device = Device() + device.CopyFrom(response) + device.device_operational_status = form.update_operational_status.data + try: + device_client.connect() + response: DeviceId = device_client.ConfigureDevice(device) + device_client.close() + flash(f'Status of device with ID "{response.device_uuid.uuid}" was updated.', 'success') + return redirect(url_for('device.home')) + except Exception as e: + flash(f'Problem updating the device. {e.details()}', 'danger') return render_template('device/update.html', device=response, form=form, submit_text='Update Device') diff --git a/src/webui/service/templates/device/update.html b/src/webui/service/templates/device/update.html index 3b25da9c1..8c474f525 100644 --- a/src/webui/service/templates/device/update.html +++ b/src/webui/service/templates/device/update.html @@ -4,19 +4,20 @@

Update Device {{ device.device_id.device_uuid.uuid }}


+ {{ form.hidden_tag() }}
- {{ form.config_operational_status.label(class="col-sm-2 col-form-label") }} + {{ form.update_operational_status.label(class="col-sm-2 col-form-label") }}
- {% if form.config_operational_status.errors %} - {{ form.config_operational_status(class="form-control is-invalid") }} + {% if form.update_operational_status.errors %} + {{ form.update_operational_status(class="form-control is-invalid") }}
- {% for error in form.config_operational_status.errors %} + {% for error in form.update_operational_status.errors %} {{ error }} {% endfor %}
{% else %} - {{ form.config_operational_status(class="form-select") }} + {{ form.update_operational_status(class="form-select") }} {% endif %}
-- GitLab From 32e1a14f0adc27e6ca5524fbfdf6ce5bd42ac7e5 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 15 Dec 2022 11:40:08 +0000 Subject: [PATCH 083/353] Common - Method Wrappers: - improved README file - enabled configuration of histogram buckets - updated emulated and openconfig device driver - updated L2NM, L3NM and TAPI service handlers --- src/common/method_wrappers/Decorator.py | 7 ++- src/common/method_wrappers/tests/README.md | 44 ++++++++++++------- .../drivers/emulated/EmulatedDriver.py | 18 +++++++- .../drivers/openconfig/OpenConfigDriver.py | 18 +++++++- .../L2NMEmulatedServiceHandler.py | 17 ++++++- .../L3NMEmulatedServiceHandler.py | 17 ++++++- .../L3NMOpenConfigServiceHandler.py | 17 ++++++- 7 files changed, 116 insertions(+), 22 deletions(-) diff --git a/src/common/method_wrappers/Decorator.py b/src/common/method_wrappers/Decorator.py index 01c256ff6..7ee2a919e 100644 --- a/src/common/method_wrappers/Decorator.py +++ b/src/common/method_wrappers/Decorator.py @@ -45,10 +45,14 @@ class MetricsPool: lock = threading.Lock() metrics : Dict[str, MetricWrapperBase] = dict() - def __init__(self, component : str, sub_module : str, labels : Dict[str, str] = {}) -> None: + def __init__( + self, component : str, sub_module : str, labels : Dict[str, str] = {}, + default_metric_params : Dict[MetricTypeEnum, Dict] = dict() + ) -> None: self._component = component self._sub_module = sub_module self._labels = labels + self._default_metric_params = default_metric_params def get_or_create(self, method : str, metric_type : MetricTypeEnum, **metric_params) -> MetricWrapperBase: metric_name = str(metric_type.value).format( @@ -57,6 +61,7 @@ class MetricsPool: if metric_name not in MetricsPool.metrics: metric_tuple : Tuple[MetricWrapperBase, Dict] = METRIC_TO_CLASS_PARAMS.get(metric_type) metric_class, default_metric_params = metric_tuple + if len(metric_params) == 0: metric_params = self._default_metric_params.get(metric_type, {}) if len(metric_params) == 0: metric_params = default_metric_params labels = sorted(self._labels.keys()) MetricsPool.metrics[metric_name] = metric_class(metric_name.lower(), '', labels, **metric_params) diff --git a/src/common/method_wrappers/tests/README.md b/src/common/method_wrappers/tests/README.md index b0db20bd9..db9c06870 100644 --- a/src/common/method_wrappers/tests/README.md +++ b/src/common/method_wrappers/tests/README.md @@ -5,33 +5,45 @@ - enable prometheus addon: ``` tfs@tfs-vm:~/tfs-ctrl$ microk8s.enable prometheus -tfs@tfs-vm:~/tfs-ctrl$ microk8s.status --wait-ready +``` + +- wait till prometheus becomes enabled (when enabled, press Ctrl+C): +``` +tfs@tfs-vm:~/tfs-ctrl$ watch -n 1 microk8s.status --wait-ready +``` + +- wait till all pods in the monitoring namespace have STATE=Running and READY=X/X (when done, press Ctrl+C): +``` +tfs@tfs-vm:~/tfs-ctrl$ watch -n 1 kubectl get pods --all-namespaces ``` - deploy as: ``` -tfs@tfs-vm:~/tfs-ctrl$ source src/common/method_wrappers/tests/deploy_specs.sh -tfs@tfs-vm:~/tfs-ctrl$ ./deploy.sh +tfs@tfs-vm:~/tfs-ctrl$ source src/common/method_wrappers/tests/deploy_specs.sh +tfs@tfs-vm:~/tfs-ctrl$ ./deploy.sh ``` - expose prometheus and grafana + - (required) terminal 1 (grafana UI): `kubectl port-forward -n monitoring service/grafana --address 0.0.0.0 3001:3000` + - (optional) terminal 2 (prometheus UI): `kubectl port-forward -n monitoring service/prometheus-k8s --address 0.0.0.0 9090:9090` + - (optional) terminal 3 (alertmanager UI): `kubectl port-forward -n monitoring service/alertmanager-main --address 0.0.0.0 9093:9093` - - terminal 1 (prometheus UI): `kubectl port-forward -n monitoring service/prometheus-k8s --address 0.0.0.0 9090:9090` - - terminal 2 (grafana UI): `kubectl port-forward -n monitoring service/grafana --address 0.0.0.0 3001:3000` - - terminal 3 (alertmanager UI): `kubectl port-forward -n monitoring service/alertmanager-main --address 0.0.0.0 9093:9093` - -- if using remote server/VM for running MicroK8s and VSCode, forward ports 9090, 3000, 9093 +- if using remote server/VM for running MicroK8s and VSCode, forward ports 3001, 9090, 9093 -terminal 4 (tun test_set): -``` -export PYTHONPATH=/home/tfs/tfs-ctrl/src -python -m common.method_wrappers.tests.test_set -``` +- (only used for internal framework debugging) run manual tests over the performance evaluation framework + - terminal 4: + ``` + export PYTHONPATH=/home/tfs/tfs-ctrl/src + python -m common.method_wrappers.tests + ``` - log into grafana: - - 127.0.0.1:3000 - - admin/admin - - upload dashboard_prometheus_histogram.json + - browse: http://127.0.0.1:3000 + - user/pass: admin/admin + - upload dashboards through "left menu > Dashboards > Manage > Import" + - upload grafana_prometheus_component_rpc.json + - upload grafana_prometheus_device_driver.json + - upload grafana_prometheus_service_handler.json - watch in real time the dashboard - upload topology through WebUI and navigate diff --git a/src/device/service/drivers/emulated/EmulatedDriver.py b/src/device/service/drivers/emulated/EmulatedDriver.py index e980bbc43..6029ff660 100644 --- a/src/device/service/drivers/emulated/EmulatedDriver.py +++ b/src/device/service/drivers/emulated/EmulatedDriver.py @@ -19,7 +19,7 @@ from apscheduler.executors.pool import ThreadPoolExecutor from apscheduler.job import Job from apscheduler.jobstores.memory import MemoryJobStore from apscheduler.schedulers.background import BackgroundScheduler -from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, metered_subclass_method, INF from common.type_checkers.Checkers import chk_float, chk_length, chk_string, chk_type from device.service.database.KpiSampleType import ORM_KpiSampleTypeEnum, grpc_to_enum__kpi_sample_type from device.service.driver_api._Driver import ( @@ -123,7 +123,23 @@ def do_sampling( value = abs(0.95 * waveform + 0.05 * noise) out_samples.put_nowait((timestamp, resource_key, value)) +HISTOGRAM_BUCKETS = ( + # .005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, INF + 0.0001, 0.00025, 0.00050, 0.00075, + 0.0010, 0.0025, 0.0050, 0.0075, + 0.0100, 0.0250, 0.0500, 0.0750, + 0.1000, 0.2500, 0.5000, 0.7500, + 1.0000, 2.5000, 5.0000, 7.5000, + 10.0, 25.0, 50.0, 75.0, + 100.0, INF +) METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': 'emulated'}) +METRICS_POOL.get_or_create('GetInitialConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('GetConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('SetConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('DeleteConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('SubscribeState', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('UnsubscribeState', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) class EmulatedDriver(_Driver): def __init__(self, address : str, port : int, **settings) -> None: # pylint: disable=super-init-not-called diff --git a/src/device/service/drivers/openconfig/OpenConfigDriver.py b/src/device/service/drivers/openconfig/OpenConfigDriver.py index 5ecf613e2..4aa42b180 100644 --- a/src/device/service/drivers/openconfig/OpenConfigDriver.py +++ b/src/device/service/drivers/openconfig/OpenConfigDriver.py @@ -21,7 +21,7 @@ from apscheduler.job import Job from apscheduler.jobstores.memory import MemoryJobStore from apscheduler.schedulers.background import BackgroundScheduler from ncclient.manager import Manager, connect_ssh -from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, metered_subclass_method, INF from common.tools.client.RetryDecorator import delay_exponential from common.type_checkers.Checkers import chk_length, chk_string, chk_type, chk_float from device.service.driver_api.Exceptions import UnsupportedResourceKeyException @@ -223,7 +223,23 @@ def edit_config( results[i] = e # if validation fails, store the exception return results +HISTOGRAM_BUCKETS = ( + # .005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, INF + 0.0001, 0.00025, 0.00050, 0.00075, + 0.0010, 0.0025, 0.0050, 0.0075, + 0.0100, 0.0250, 0.0500, 0.0750, + 0.1000, 0.2500, 0.5000, 0.7500, + 1.0000, 2.5000, 5.0000, 7.5000, + 10.0, 25.0, 50.0, 75.0, + 100.0, INF +) METRICS_POOL = MetricsPool('Device', 'Driver', labels={'driver': 'openconfig'}) +METRICS_POOL.get_or_create('GetInitialConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('GetConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('SetConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('DeleteConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('SubscribeState', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('UnsubscribeState', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) class OpenConfigDriver(_Driver): def __init__(self, address : str, port : int, **settings) -> None: # pylint: disable=super-init-not-called diff --git a/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py b/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py index 69e61db41..bc628c160 100644 --- a/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py +++ b/src/service/service/service_handlers/l2nm_emulated/L2NMEmulatedServiceHandler.py @@ -14,7 +14,7 @@ import anytree, json, logging from typing import Any, List, Optional, Tuple, Union -from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, metered_subclass_method, INF from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service from common.tools.object_factory.Device import json_device_id from common.type_checkers.Checkers import chk_length, chk_type @@ -25,7 +25,22 @@ from .ConfigRules import setup_config_rules, teardown_config_rules LOGGER = logging.getLogger(__name__) +HISTOGRAM_BUCKETS = ( + # .005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, INF + 0.0010, 0.0025, 0.0050, 0.0075, + 0.0100, 0.0250, 0.0500, 0.0750, + 0.1000, 0.2500, 0.5000, 0.7500, + 1.0000, 2.5000, 5.0000, 7.5000, + 10.0000, 25.000, 50.0000, 75.000, + 100.0, INF +) METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l2nm_emulated'}) +METRICS_POOL.get_or_create('SetEndpoint', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('DeleteEndpoint', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('SetConstraint', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('DeleteConstraint', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('SetConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('DeleteConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) class L2NMEmulatedServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called diff --git a/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py b/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py index dca5942ac..f16122519 100644 --- a/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py +++ b/src/service/service/service_handlers/l3nm_emulated/L3NMEmulatedServiceHandler.py @@ -14,7 +14,7 @@ import anytree, json, logging from typing import Any, List, Optional, Tuple, Union -from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, metered_subclass_method, INF from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service from common.tools.object_factory.Device import json_device_id from common.type_checkers.Checkers import chk_length, chk_type @@ -25,7 +25,22 @@ from .ConfigRules import setup_config_rules, teardown_config_rules LOGGER = logging.getLogger(__name__) +HISTOGRAM_BUCKETS = ( + # .005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, INF + 0.0010, 0.0025, 0.0050, 0.0075, + 0.0100, 0.0250, 0.0500, 0.0750, + 0.1000, 0.2500, 0.5000, 0.7500, + 1.0000, 2.5000, 5.0000, 7.5000, + 10.0000, 25.000, 50.0000, 75.000, + 100.0, INF +) METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l3nm_emulated'}) +METRICS_POOL.get_or_create('SetEndpoint', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('DeleteEndpoint', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('SetConstraint', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('DeleteConstraint', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('SetConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('DeleteConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) class L3NMEmulatedServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called diff --git a/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py b/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py index 76a335745..0f5cb6c55 100644 --- a/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py +++ b/src/service/service/service_handlers/l3nm_openconfig/L3NMOpenConfigServiceHandler.py @@ -14,7 +14,7 @@ import anytree, json, logging from typing import Any, List, Optional, Tuple, Union -from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method +from common.method_wrappers.Decorator import MetricTypeEnum, MetricsPool, metered_subclass_method, INF from common.proto.context_pb2 import ConfigActionEnum, ConfigRule, DeviceId, Service from common.tools.object_factory.Device import json_device_id from common.type_checkers.Checkers import chk_length, chk_type @@ -25,7 +25,22 @@ from .ConfigRules import setup_config_rules, teardown_config_rules LOGGER = logging.getLogger(__name__) +HISTOGRAM_BUCKETS = ( + # .005, .01, .025, .05, .075, .1, .25, .5, .75, 1.0, INF + 0.0010, 0.0025, 0.0050, 0.0075, + 0.0100, 0.0250, 0.0500, 0.0750, + 0.1000, 0.2500, 0.5000, 0.7500, + 1.0000, 2.5000, 5.0000, 7.5000, + 10.0000, 25.000, 50.0000, 75.000, + 100.0, INF +) METRICS_POOL = MetricsPool('Service', 'Handler', labels={'handler': 'l3nm_openconfig'}) +METRICS_POOL.get_or_create('SetEndpoint', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('DeleteEndpoint', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('SetConstraint', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('DeleteConstraint', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('SetConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) +METRICS_POOL.get_or_create('DeleteConfig', MetricTypeEnum.HISTOGRAM_DURATION, buckets=HISTOGRAM_BUCKETS) class L3NMOpenConfigServiceHandler(_ServiceHandler): def __init__( # pylint: disable=super-init-not-called -- GitLab From 817f5f08825e4999257b85823e6fac8af5dafa17 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 16 Dec 2022 00:04:53 +0100 Subject: [PATCH 084/353] Proto: - added field name in context - added field name in topology - added field name in device - added field name in link - added field name in service - added field name in slice --- proto/context.proto | 73 +++++++++++---------- src/common/Constants.py | 13 ++-- src/common/tools/object_factory/Context.py | 9 ++- src/common/tools/object_factory/Topology.py | 6 +- 4 files changed, 58 insertions(+), 43 deletions(-) diff --git a/proto/context.proto b/proto/context.proto index 3f0532d23..db0c81381 100644 --- a/proto/context.proto +++ b/proto/context.proto @@ -101,9 +101,11 @@ message ContextId { message Context { ContextId context_id = 1; - repeated TopologyId topology_ids = 2; - repeated ServiceId service_ids = 3; - TeraFlowController controller = 4; + string name = 2; + repeated TopologyId topology_ids = 3; + repeated ServiceId service_ids = 4; + repeated SliceId slice_ids = 5; + TeraFlowController controller = 6; } message ContextIdList { @@ -128,8 +130,9 @@ message TopologyId { message Topology { TopologyId topology_id = 1; - repeated DeviceId device_ids = 2; - repeated LinkId link_ids = 3; + string name = 2; + repeated DeviceId device_ids = 3; + repeated LinkId link_ids = 4; } message TopologyIdList { @@ -153,12 +156,13 @@ message DeviceId { message Device { DeviceId device_id = 1; - string device_type = 2; - DeviceConfig device_config = 3; - DeviceOperationalStatusEnum device_operational_status = 4; - repeated DeviceDriverEnum device_drivers = 5; - repeated EndPoint device_endpoints = 6; - repeated Component component = 7; // Used for inventory + string name = 2; + string device_type = 3; + DeviceConfig device_config = 4; + DeviceOperationalStatusEnum device_operational_status = 5; + repeated DeviceDriverEnum device_drivers = 6; + repeated EndPoint device_endpoints = 7; + repeated Component component = 8; // Used for inventory } message Component { @@ -207,7 +211,8 @@ message LinkId { message Link { LinkId link_id = 1; - repeated EndPointId link_endpoint_ids = 2; + string name = 2; + repeated EndPointId link_endpoint_ids = 3; } message LinkIdList { @@ -232,12 +237,13 @@ message ServiceId { message Service { ServiceId service_id = 1; - ServiceTypeEnum service_type = 2; - repeated EndPointId service_endpoint_ids = 3; - repeated Constraint service_constraints = 4; - ServiceStatus service_status = 5; - ServiceConfig service_config = 6; - Timestamp timestamp = 7; + string name = 2; + ServiceTypeEnum service_type = 3; + repeated EndPointId service_endpoint_ids = 4; + repeated Constraint service_constraints = 5; + ServiceStatus service_status = 6; + ServiceConfig service_config = 7; + Timestamp timestamp = 8; } enum ServiceTypeEnum { @@ -284,14 +290,15 @@ message SliceId { message Slice { SliceId slice_id = 1; - repeated EndPointId slice_endpoint_ids = 2; - repeated Constraint slice_constraints = 3; - repeated ServiceId slice_service_ids = 4; - repeated SliceId slice_subslice_ids = 5; - SliceStatus slice_status = 6; - SliceConfig slice_config = 7; - SliceOwner slice_owner = 8; - Timestamp timestamp = 9; + string name = 2; + repeated EndPointId slice_endpoint_ids = 3; + repeated Constraint slice_constraints = 4; + repeated ServiceId slice_service_ids = 5; + repeated SliceId slice_subslice_ids = 6; + SliceStatus slice_status = 7; + SliceConfig slice_config = 8; + SliceOwner slice_owner = 9; + Timestamp timestamp = 10; } message SliceOwner { @@ -300,11 +307,11 @@ message SliceOwner { } enum SliceStatusEnum { - SLICESTATUS_UNDEFINED = 0; - SLICESTATUS_PLANNED = 1; - SLICESTATUS_INIT = 2; - SLICESTATUS_ACTIVE = 3; - SLICESTATUS_DEINIT = 4; + SLICESTATUS_UNDEFINED = 0; + SLICESTATUS_PLANNED = 1; + SLICESTATUS_INIT = 2; + SLICESTATUS_ACTIVE = 3; + SLICESTATUS_DEINIT = 4; SLICESTATUS_SLA_VIOLATED = 5; } @@ -409,8 +416,8 @@ message EndPoint { // ----- Configuration ------------------------------------------------------------------------------------------------- enum ConfigActionEnum { CONFIGACTION_UNDEFINED = 0; - CONFIGACTION_SET = 1; - CONFIGACTION_DELETE = 2; + CONFIGACTION_SET = 1; + CONFIGACTION_DELETE = 2; } message ConfigRule_Custom { diff --git a/src/common/Constants.py b/src/common/Constants.py index 5558ef25d..d606c0d03 100644 --- a/src/common/Constants.py +++ b/src/common/Constants.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging +import logging, uuid from enum import Enum # Default logging level @@ -30,11 +30,12 @@ DEFAULT_HTTP_BIND_ADDRESS = '0.0.0.0' DEFAULT_METRICS_PORT = 9192 # Default context and topology UUIDs -#DEFAULT_CONTEXT_UUID = '85f78267-4c5e-4f80-ad2f-7fbaca7c62a0' -#DEFAULT_TOPOLOGY_UUID = '85f78267-4c5e-4f80-ad2f-7fbaca7c62a0' -DEFAULT_CONTEXT_UUID = 'admin' -DEFAULT_TOPOLOGY_UUID = 'admin' # contains the detailed local topology -INTERDOMAIN_TOPOLOGY_UUID = 'inter' # contains the abstract inter-domain topology +DEFAULT_CONTEXT_NAME = 'admin' +DEFAULT_TOPOLOGY_NAME = 'admin' # contains the detailed local topology +INTERDOMAIN_TOPOLOGY_NAME = 'inter' # contains the abstract inter-domain topology +DEFAULT_CONTEXT_UUID = str(uuid.uuid5(uuid.NAMESPACE_OID, DEFAULT_CONTEXT_NAME )) +DEFAULT_TOPOLOGY_UUID = str(uuid.uuid5(uuid.NAMESPACE_OID, DEFAULT_TOPOLOGY_NAME )) +INTERDOMAIN_TOPOLOGY_UUID = str(uuid.uuid5(uuid.NAMESPACE_OID, INTERDOMAIN_TOPOLOGY_NAME)) # Default service names class ServiceNameEnum(Enum): diff --git a/src/common/tools/object_factory/Context.py b/src/common/tools/object_factory/Context.py index d5d1bf943..58f35b929 100644 --- a/src/common/tools/object_factory/Context.py +++ b/src/common/tools/object_factory/Context.py @@ -12,12 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +from typing import Optional + def json_context_id(context_uuid : str): return {'context_uuid': {'uuid': context_uuid}} -def json_context(context_uuid : str): - return { +def json_context(context_uuid : str, name : Optional[str] = None): + result = { 'context_id' : json_context_id(context_uuid), 'topology_ids': [], 'service_ids' : [], + 'slice_ids' : [], } + if name is not None: result['name'] = name + return result diff --git a/src/common/tools/object_factory/Topology.py b/src/common/tools/object_factory/Topology.py index 7de4a1d57..5f7a42d7a 100644 --- a/src/common/tools/object_factory/Topology.py +++ b/src/common/tools/object_factory/Topology.py @@ -20,9 +20,11 @@ def json_topology_id(topology_uuid : str, context_id : Optional[Dict] = None): if context_id is not None: result['context_id'] = copy.deepcopy(context_id) return result -def json_topology(topology_uuid : str, context_id : Optional[Dict] = None): - return { +def json_topology(topology_uuid : str, name : Optional[str] = None, context_id : Optional[Dict] = None): + result = { 'topology_id': json_topology_id(topology_uuid, context_id=context_id), 'device_ids' : [], 'link_ids' : [], } + if name is not None: result['name'] = name + return result -- GitLab From 6cf2056a321c1751f24b4383dedff9b15133d56e Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 16 Dec 2022 00:09:13 +0100 Subject: [PATCH 085/353] Context component: - updatd EventsCollector get_events - added field created_at in ContextModel - added ChangeFeedClient - WIP arrangements in unitary tests - WIP arrangements in ServicerImpl - arranged run_tests_locally script --- scripts/run_tests_locally-context.sh | 20 +- src/context/client/EventsCollector.py | 2 +- src/context/service/ChangeFeedClient.py | 87 ++++++ .../service/ContextServiceServicerImpl.py | 111 ++++--- src/context/service/Database.py | 23 +- src/context/service/Engine.py | 8 +- src/context/service/database/ConfigModel.py | 2 +- src/context/service/database/ContextModel.py | 3 +- src/context/service/database/__init__.py | 1 + src/context/tests/test_unitary.py | 293 ++++++++++-------- 10 files changed, 356 insertions(+), 194 deletions(-) create mode 100644 src/context/service/ChangeFeedClient.py diff --git a/scripts/run_tests_locally-context.sh b/scripts/run_tests_locally-context.sh index 7033fcb01..bf0cccd6b 100755 --- a/scripts/run_tests_locally-context.sh +++ b/scripts/run_tests_locally-context.sh @@ -20,7 +20,7 @@ # If not already set, set the name of the Kubernetes namespace to deploy to. export TFS_K8S_NAMESPACE=${TFS_K8S_NAMESPACE:-"tfs"} -export TFS_K8S_HOSTNAME="tfs-vm" +#export TFS_K8S_HOSTNAME="tfs-vm" ######################################################################################################################## # Automated steps start here @@ -29,15 +29,21 @@ export TFS_K8S_HOSTNAME="tfs-vm" PROJECTDIR=`pwd` cd $PROJECTDIR/src -RCFILE=$PROJECTDIR/coverage/.coveragerc +#RCFILE=$PROJECTDIR/coverage/.coveragerc -kubectl --namespace $TFS_K8S_NAMESPACE expose deployment contextservice --name=redis-tests --port=6379 --type=NodePort +#kubectl --namespace $TFS_K8S_NAMESPACE expose deployment contextservice --name=redis-tests --port=6379 --type=NodePort #export REDIS_SERVICE_HOST=$(kubectl --namespace $TFS_K8S_NAMESPACE get service redis-tests -o 'jsonpath={.spec.clusterIP}') -export REDIS_SERVICE_HOST=$(kubectl get node $TFS_K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') -export REDIS_SERVICE_PORT=$(kubectl --namespace $TFS_K8S_NAMESPACE get service redis-tests -o 'jsonpath={.spec.ports[?(@.port==6379)].nodePort}') +#export REDIS_SERVICE_HOST=$(kubectl get node $TFS_K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') +#export REDIS_SERVICE_PORT=$(kubectl --namespace $TFS_K8S_NAMESPACE get service redis-tests -o 'jsonpath={.spec.ports[?(@.port==6379)].nodePort}') + +export CRDB_URI="cockroachdb://tfs:tfs123@127.0.0.1:26257/tfs?sslmode=require" # Run unitary tests and analyze coverage of code at same time -coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose --maxfail=1 \ +#coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose --maxfail=1 \ +# context/tests/test_unitary.py + +source tfs_runtime_env_vars.sh +pytest --log-level=INFO --verbose -o log_cli=true --maxfail=1 \ context/tests/test_unitary.py -kubectl --namespace $TFS_K8S_NAMESPACE delete service redis-tests +#kubectl --namespace $TFS_K8S_NAMESPACE delete service redis-tests diff --git a/src/context/client/EventsCollector.py b/src/context/client/EventsCollector.py index f5fc3fbc7..9ad6e101b 100644 --- a/src/context/client/EventsCollector.py +++ b/src/context/client/EventsCollector.py @@ -132,7 +132,7 @@ class EventsCollector: if event is None: break events.append(event) else: - for _ in range(count): + while len(events) < count: if self._terminate.is_set(): break event = self.get_event(block=block, timeout=timeout) if event is None: continue diff --git a/src/context/service/ChangeFeedClient.py b/src/context/service/ChangeFeedClient.py new file mode 100644 index 000000000..8285dc6c3 --- /dev/null +++ b/src/context/service/ChangeFeedClient.py @@ -0,0 +1,87 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +# pip install psycopg==3.1.6 +# Ref: https://www.cockroachlabs.com/docs/stable/changefeed-for.html +# (current implementation) Ref: https://www.cockroachlabs.com/docs/v22.1/changefeed-for +# Ref: https://www.psycopg.org/psycopg3/docs/api/crdb.html + +import contextlib, json, logging, psycopg, psycopg.conninfo, psycopg.crdb, sys, time +from typing import Any, Dict, Iterator, List, Optional, Tuple +from common.Settings import get_setting + +LOGGER = logging.getLogger(__name__) + +SQL_ACTIVATE_CHANGE_FEED = 'SET CLUSTER SETTING kv.rangefeed.enabled = true' +SQL_START_CHANGE_FEED = 'EXPERIMENTAL CHANGEFEED FOR {:s}.{:s} WITH format=json, no_initial_scan, updated' + +class ChangeFeedClient: + def __init__(self) -> None: + self._connection : Optional[psycopg.crdb.CrdbConnection] = None + self._conn_info_dict : Dict = dict() + self._is_crdb : bool = False + + def initialize(self) -> bool: + crdb_uri = get_setting('CRDB_URI') + if crdb_uri is None: + LOGGER.error('Connection string not found in EnvVar CRDB_URI') + return False + + try: + crdb_uri = crdb_uri.replace('cockroachdb://', 'postgres://') + self._conn_info_dict = psycopg.conninfo.conninfo_to_dict(crdb_uri) + except psycopg.ProgrammingError: + LOGGER.exception('Invalid connection string: {:s}'.format(str(crdb_uri))) + return False + + self._connection = psycopg.crdb.connect(**self._conn_info_dict) + self._is_crdb = psycopg.crdb.CrdbConnection.is_crdb(self._connection) + LOGGER.debug('is_crdb = {:s}'.format(str(self._is_crdb))) + + # disable multi-statement transactions + self._connection.autocommit = True + + # activate change feeds + self._connection.execute(SQL_ACTIVATE_CHANGE_FEED) + + return self._is_crdb + + def get_changes(self, table_name : str) -> Iterator[Tuple[float, str, List[Any], bool, Dict]]: + db_name = self._conn_info_dict.get('dbname') + if db_name is None: raise Exception('ChangeFeed has not been initialized!') + cur = self._connection.cursor() + str_sql_query = SQL_START_CHANGE_FEED.format(db_name, table_name) + with contextlib.closing(cur.stream(str_sql_query)) as feed: + for change in feed: + LOGGER.info(change) + table_name, primary_key, data = change[0], json.loads(change[1]), json.loads(change[2]) + timestamp = data.get('updated') / 1.e9 + if timestamp is None: timestamp = time.time() + after = data.get('after') + is_delete = ('after' in data) and (after is None) + yield timestamp, table_name, primary_key, is_delete, after + +def main(): + logging.basicConfig(level=logging.INFO) + + cf = ChangeFeed() + ready = cf.initialize() + if not ready: raise Exception('Unable to initialize ChangeFeed') + for change in cf.get_changes('context'): + LOGGER.info(change) + + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py index b5725f007..fcb0024d2 100644 --- a/src/context/service/ContextServiceServicerImpl.py +++ b/src/context/service/ContextServiceServicerImpl.py @@ -13,13 +13,13 @@ # limitations under the License. -import grpc, json, logging, operator, sqlalchemy, threading, uuid +import grpc, json, logging, operator, sqlalchemy, threading, time, uuid from sqlalchemy.orm import Session, contains_eager, selectinload, sessionmaker -from sqlalchemy.dialects.postgresql import UUID, insert +#from sqlalchemy.dialects.postgresql import UUID, insert from sqlalchemy_cockroachdb import run_transaction from typing import Dict, Iterator, List, Optional, Set, Tuple, Union from common.message_broker.MessageBroker import MessageBroker -from common.orm.backend.Tools import key_to_str +#from common.orm.backend.Tools import key_to_str from common.proto.context_pb2 import ( Connection, ConnectionEvent, ConnectionId, ConnectionIdList, ConnectionList, Context, ContextEvent, ContextId, ContextIdList, ContextList, @@ -30,36 +30,39 @@ from common.proto.context_pb2 import ( Slice, SliceEvent, SliceId, SliceIdList, SliceList, Topology, TopologyEvent, TopologyId, TopologyIdList, TopologyList, ConfigActionEnum, Constraint) -from common.proto.policy_pb2 import PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule +#from common.proto.policy_pb2 import PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule from common.proto.context_pb2_grpc import ContextServiceServicer from common.proto.context_policy_pb2_grpc import ContextPolicyServiceServicer +from common.tools.object_factory.Context import json_context_id from common.rpc_method_wrapper.Decorator import create_metrics, safe_and_metered_rpc_method -from common.rpc_method_wrapper.ServiceExceptions import InvalidArgumentException, NotFoundException -from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string -from context.service.Database import Database -from context.service.database.ConfigModel import ( - ConfigModel, ORM_ConfigActionEnum, ConfigRuleModel, grpc_config_rules_to_raw, update_config) -from context.service.database.ConnectionModel import ConnectionModel, set_path -from context.service.database.ConstraintModel import ( - ConstraintModel, ConstraintsModel, Union_ConstraintModel, CONSTRAINT_PARSERS, set_constraints) +from common.rpc_method_wrapper.ServiceExceptions import ( + InvalidArgumentException, NotFoundException, OperationFailedException) +#from common.tools.grpc.Tools import grpc_message_to_json, grpc_message_to_json_string +#from context.service.Database import Database +#from context.service.database.ConfigModel import ( +# ConfigModel, ORM_ConfigActionEnum, ConfigRuleModel, grpc_config_rules_to_raw, update_config) +#from context.service.database.ConnectionModel import ConnectionModel, set_path +#from context.service.database.ConstraintModel import ( +# ConstraintModel, ConstraintsModel, Union_ConstraintModel, CONSTRAINT_PARSERS, set_constraints) from context.service.database.ContextModel import ContextModel -from context.service.database.DeviceModel import ( - DeviceModel, grpc_to_enum__device_operational_status, set_drivers, grpc_to_enum__device_driver, DriverModel) -from context.service.database.EndPointModel import EndPointModel, KpiSampleTypeModel, set_kpi_sample_types -from context.service.database.Events import notify_event -from context.service.database.KpiSampleType import grpc_to_enum__kpi_sample_type -from context.service.database.LinkModel import LinkModel -from context.service.database.PolicyRuleModel import PolicyRuleModel -from context.service.database.RelationModels import ( - ConnectionSubServiceModel, LinkEndPointModel, ServiceEndPointModel, SliceEndPointModel, SliceServiceModel, - SliceSubSliceModel, TopologyDeviceModel, TopologyLinkModel) -from context.service.database.ServiceModel import ( - ServiceModel, grpc_to_enum__service_status, grpc_to_enum__service_type) -from context.service.database.SliceModel import SliceModel, grpc_to_enum__slice_status -from context.service.database.TopologyModel import TopologyModel -from .Constants import ( - CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE, - TOPIC_TOPOLOGY) +#from context.service.database.DeviceModel import ( +# DeviceModel, grpc_to_enum__device_operational_status, set_drivers, grpc_to_enum__device_driver, DriverModel) +#from context.service.database.EndPointModel import EndPointModel, KpiSampleTypeModel, set_kpi_sample_types +#from context.service.database.Events import notify_event +#from context.service.database.KpiSampleType import grpc_to_enum__kpi_sample_type +#from context.service.database.LinkModel import LinkModel +#from context.service.database.PolicyRuleModel import PolicyRuleModel +#from context.service.database.RelationModels import ( +# ConnectionSubServiceModel, LinkEndPointModel, ServiceEndPointModel, SliceEndPointModel, SliceServiceModel, +# SliceSubSliceModel, TopologyDeviceModel, TopologyLinkModel) +#from context.service.database.ServiceModel import ( +# ServiceModel, grpc_to_enum__service_status, grpc_to_enum__service_type) +#from context.service.database.SliceModel import SliceModel, grpc_to_enum__slice_status +#from context.service.database.TopologyModel import TopologyModel +#from .Constants import ( +# CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE, +# TOPIC_TOPOLOGY) +from .ChangeFeedClient import ChangeFeedClient LOGGER = logging.getLogger(__name__) @@ -106,7 +109,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer @safe_and_metered_rpc_method(METRICS, LOGGER) def GetContext(self, request: ContextId, context : grpc.ServicerContext) -> Context: - context_uuid = str(uuid.uuid5(uuid.NAMESPACE_OID, request.context_uuid.uuid)) + context_uuid = request.context_uuid.uuid def callback(session : Session) -> Optional[Dict]: obj : Optional[ContextModel] = \ session.query(ContextModel).filter_by(context_uuid=context_uuid).one_or_none() @@ -117,8 +120,8 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer @safe_and_metered_rpc_method(METRICS, LOGGER) def SetContext(self, request: Context, context : grpc.ServicerContext) -> ContextId: - context_uuid = str(uuid.uuid5(uuid.NAMESPACE_OID, request.context_id.context_uuid.uuid)) - context_name = request.context_id.context_uuid.uuid + context_uuid = request.context_id.context_uuid.uuid + context_name = request.name for i, topology_id in enumerate(request.topology_ids): topology_context_uuid = topology_id.context_id.context_uuid.uuid @@ -134,15 +137,24 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer 'request.service_ids[{:d}].context_id.context_uuid.uuid'.format(i), service_context_uuid, ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)]) + for i, slice_id in enumerate(request.slice_ids): + slice_context_uuid = slice_id.context_id.context_uuid.uuid + if slice_context_uuid != context_uuid: + raise InvalidArgumentException( + 'request.slice_ids[{:d}].context_id.context_uuid.uuid'.format(i), slice_context_uuid, + ['should be == {:s}({:s})'.format('request.context_id.context_uuid.uuid', context_uuid)]) + def callback(session : Session) -> Tuple[Optional[Dict], bool]: obj : Optional[ContextModel] = \ session.query(ContextModel).with_for_update().filter_by(context_uuid=context_uuid).one_or_none() - updated = obj is not None - obj = ContextModel(context_uuid=context_uuid, context_name=context_name) - session.merge(obj) - session.commit() + is_update = obj is not None + if is_update: + obj.context_name = context_name + session.merge(obj) + else: + session.add(ContextModel(context_uuid=context_uuid, context_name=context_name, created_at=time.time())) obj = session.get(ContextModel, {'context_uuid': context_uuid}) - return (None if obj is None else obj.dump_id()), updated + return (None if obj is None else obj.dump_id()), is_update obj_id,updated = run_transaction(sessionmaker(bind=self.db_engine), callback) if obj_id is None: raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) @@ -153,7 +165,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer @safe_and_metered_rpc_method(METRICS, LOGGER) def RemoveContext(self, request: ContextId, context : grpc.ServicerContext) -> Empty: - context_uuid = str(uuid.uuid5(uuid.NAMESPACE_OID, request.context_uuid.uuid)) + context_uuid = request.context_uuid.uuid def callback(session : Session) -> bool: num_deleted = session.query(ContextModel).filter_by(context_uuid=context_uuid).delete() @@ -164,11 +176,24 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer # notify_event(self.messagebroker, TOPIC_CONTEXT, EventTypeEnum.EVENTTYPE_REMOVE, {'context_id': request}) return Empty() -# @safe_and_metered_rpc_method(METRICS, LOGGER) -# def GetContextEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ContextEvent]: -# for message in self.messagebroker.consume({TOPIC_CONTEXT}, consume_timeout=CONSUME_TIMEOUT): -# yield ContextEvent(**json.loads(message.content)) - + @safe_and_metered_rpc_method(METRICS, LOGGER) + def GetContextEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ContextEvent]: + #for message in self.messagebroker.consume({TOPIC_CONTEXT}, consume_timeout=CONSUME_TIMEOUT): + # yield ContextEvent(**json.loads(message.content)) + cf = ChangeFeedClient() + ready = cf.initialize() + if not ready: raise OperationFailedException('Initialize ChangeFeed') + for timestamp, _, primary_key, is_delete, after in cf.get_changes('context'): + if is_delete: + event_type = EventTypeEnum.EVENTTYPE_REMOVE + else: + is_create = (timestamp - after.get('created_at')) < 1.0 + event_type = EventTypeEnum.EVENTTYPE_CREATE if is_create else EventTypeEnum.EVENTTYPE_UPDATE + event = { + 'event': {'timestamp': {'timestamp': timestamp}, 'event_type': event_type}, + 'context_id': json_context_id(primary_key[0]), + } + yield ContextEvent(**event) # ----- Topology --------------------------------------------------------------------------------------------------- diff --git a/src/context/service/Database.py b/src/context/service/Database.py index 8aa568239..03598a97f 100644 --- a/src/context/service/Database.py +++ b/src/context/service/Database.py @@ -1,16 +1,13 @@ -from typing import Tuple, List - -from sqlalchemy import MetaData -from sqlalchemy.orm import Session, joinedload -from context.service.database._Base import Base import logging -from common.orm.backend.Tools import key_to_str - +from sqlalchemy import MetaData +from sqlalchemy.orm import Session #, joinedload +from typing import Tuple #, List +from context.service.database._Base import _Base +#from common.orm.backend.Tools import key_to_str from common.rpc_method_wrapper.ServiceExceptions import NotFoundException LOGGER = logging.getLogger(__name__) - class Database(Session): def __init__(self, session): super().__init__() @@ -62,8 +59,8 @@ class Database(Session): def clear(self): with self.session() as session: engine = session.get_bind() - Base.metadata.drop_all(engine) - Base.metadata.create_all(engine) + _Base.metadata.drop_all(engine) + _Base.metadata.create_all(engine) def dump_by_table(self): with self.session() as session: @@ -90,7 +87,7 @@ class Database(Session): return result - def get_object(self, model_class: Base, main_key: str, raise_if_not_found=False): + def get_object(self, model_class: _Base, main_key: str, raise_if_not_found=False): filt = {model_class.main_pk_name(): main_key} with self.session() as session: get = session.query(model_class).filter_by(**filt).one_or_none() @@ -104,7 +101,7 @@ class Database(Session): dump = get.dump() return get, dump - def get_object_filter(self, model_class: Base, filt, raise_if_not_found=False): + def get_object_filter(self, model_class: _Base, filt, raise_if_not_found=False): with self.session() as session: get = session.query(model_class).filter_by(**filt).all() @@ -119,7 +116,7 @@ class Database(Session): return get, get.dump() - def get_or_create(self, model_class: Base, key_parts: str, filt=None) -> Tuple[Base, bool]: + def get_or_create(self, model_class: _Base, key_parts: str, filt=None) -> Tuple[_Base, bool]: if not filt: filt = {model_class.main_pk_name(): key_parts} with self.session() as session: diff --git a/src/context/service/Engine.py b/src/context/service/Engine.py index 7944d8601..08e1e4f93 100644 --- a/src/context/service/Engine.py +++ b/src/context/service/Engine.py @@ -21,20 +21,20 @@ APP_NAME = 'tfs' class Engine: def get_engine(self) -> sqlalchemy.engine.Engine: - ccdb_url = get_setting('CCDB_URL') + crdb_uri = get_setting('CRDB_URI') try: engine = sqlalchemy.create_engine( - ccdb_url, connect_args={'application_name': APP_NAME}, echo=False, future=True) + crdb_uri, connect_args={'application_name': APP_NAME}, echo=False, future=True) except: # pylint: disable=bare-except - LOGGER.exception('Failed to connect to database: {:s}'.format(ccdb_url)) + LOGGER.exception('Failed to connect to database: {:s}'.format(crdb_uri)) return None try: if not sqlalchemy_utils.database_exists(engine.url): sqlalchemy_utils.create_database(engine.url) except: # pylint: disable=bare-except - LOGGER.exception('Failed to check/create to database: {:s}'.format(ccdb_url)) + LOGGER.exception('Failed to check/create to database: {:s}'.format(crdb_uri)) return None return engine diff --git a/src/context/service/database/ConfigModel.py b/src/context/service/database/ConfigModel.py index 5f7111981..d36622e76 100644 --- a/src/context/service/database/ConfigModel.py +++ b/src/context/service/database/ConfigModel.py @@ -19,7 +19,7 @@ from common.proto.context_pb2 import ConfigActionEnum from common.tools.grpc.Tools import grpc_message_to_json_string from sqlalchemy import Column, ForeignKey, INTEGER, CheckConstraint, Enum, String from sqlalchemy.dialects.postgresql import UUID, ARRAY -from context.service.database._Base import Base +from context.service.database._Base import _Base from sqlalchemy.orm import relationship from context.service.Database import Database diff --git a/src/context/service/database/ContextModel.py b/src/context/service/database/ContextModel.py index 46f0741e5..9ad5e0bcb 100644 --- a/src/context/service/database/ContextModel.py +++ b/src/context/service/database/ContextModel.py @@ -14,7 +14,7 @@ import logging from typing import Dict -from sqlalchemy import Column, String +from sqlalchemy import Column, Float, String from sqlalchemy.dialects.postgresql import UUID from ._Base import _Base #from sqlalchemy.orm import relationship @@ -25,6 +25,7 @@ class ContextModel(_Base): __tablename__ = 'context' context_uuid = Column(UUID(as_uuid=False), primary_key=True) context_name = Column(String(), nullable=False) + created_at = Column(Float) #topology = relationship('TopologyModel', back_populates='context') diff --git a/src/context/service/database/__init__.py b/src/context/service/database/__init__.py index 27b5f5dd2..980265786 100644 --- a/src/context/service/database/__init__.py +++ b/src/context/service/database/__init__.py @@ -13,3 +13,4 @@ # limitations under the License. from ._Base import _Base, rebuild_database +from .ContextModel import ContextModel diff --git a/src/context/tests/test_unitary.py b/src/context/tests/test_unitary.py index aaa8c7fbd..8bf1b4ff1 100644 --- a/src/context/tests/test_unitary.py +++ b/src/context/tests/test_unitary.py @@ -13,7 +13,7 @@ # limitations under the License. # pylint: disable=too-many-lines -import copy, grpc, logging, os, pytest, requests, time, urllib +import copy, grpc, logging, os, pytest, requests, sqlalchemy, time, urllib, uuid from typing import Tuple from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID, ServiceNameEnum from common.Settings import ( @@ -27,6 +27,10 @@ from common.proto.context_pb2 import ( DeviceOperationalStatusEnum, Empty, EventTypeEnum, Link, LinkEvent, LinkId, Service, ServiceEvent, ServiceId, ServiceStatusEnum, ServiceTypeEnum, Topology, TopologyEvent, TopologyId) from common.proto.policy_pb2 import (PolicyRuleIdList, PolicyRuleId, PolicyRuleList, PolicyRule) +from common.tools.object_factory.Context import json_context, json_context_id +from common.tools.object_factory.Service import json_service_id +from common.tools.object_factory.Slice import json_slice_id +from common.tools.object_factory.Topology import json_topology_id from common.type_checkers.Assertions import ( validate_connection, validate_connection_ids, validate_connections, validate_context, validate_context_ids, validate_contexts, validate_device, validate_device_ids, validate_devices, validate_link, validate_link_ids, @@ -36,14 +40,17 @@ from context.client.ContextClient import ContextClient from context.client.EventsCollector import EventsCollector from context.service.database.Tools import ( FASTHASHER_DATA_ACCEPTED_FORMAT, FASTHASHER_ITEM_ACCEPTED_FORMAT, fast_hasher) -from context.service.grpc_server.ContextService import ContextService -from context.service._old_code.Populate import populate -from context.service.rest_server.RestServer import RestServer -from context.service.rest_server.Resources import RESOURCES +from context.service.ContextService import ContextService +#from context.service._old_code.Populate import populate +#from context.service.rest_server.RestServer import RestServer +#from context.service.rest_server.Resources import RESOURCES from requests import Session from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from context.service.database._Base import Base +from context.service.database._Base import _Base +from common.Settings import get_setting +from context.service.Engine import Engine +from context.service.database._Base import rebuild_database from .Objects import ( CONNECTION_R1_R3, CONNECTION_R1_R3_ID, CONNECTION_R1_R3_UUID, CONTEXT, CONTEXT_ID, DEVICE_R1, DEVICE_R1_ID, @@ -63,90 +70,86 @@ os.environ[get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_HOST os.environ[get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_GRPC)] = str(GRPC_PORT) os.environ[get_env_var_name(ServiceNameEnum.CONTEXT, ENVVAR_SUFIX_SERVICE_PORT_HTTP)] = str(HTTP_PORT) -DEFAULT_REDIS_SERVICE_HOST = LOCAL_HOST -DEFAULT_REDIS_SERVICE_PORT = 6379 -DEFAULT_REDIS_DATABASE_ID = 0 +#DEFAULT_REDIS_SERVICE_HOST = LOCAL_HOST +#DEFAULT_REDIS_SERVICE_PORT = 6379 +#DEFAULT_REDIS_DATABASE_ID = 0 -REDIS_CONFIG = { - 'REDIS_SERVICE_HOST': os.environ.get('REDIS_SERVICE_HOST', DEFAULT_REDIS_SERVICE_HOST), - 'REDIS_SERVICE_PORT': os.environ.get('REDIS_SERVICE_PORT', DEFAULT_REDIS_SERVICE_PORT), - 'REDIS_DATABASE_ID' : os.environ.get('REDIS_DATABASE_ID', DEFAULT_REDIS_DATABASE_ID ), -} +#REDIS_CONFIG = { +# 'REDIS_SERVICE_HOST': os.environ.get('REDIS_SERVICE_HOST', DEFAULT_REDIS_SERVICE_HOST), +# 'REDIS_SERVICE_PORT': os.environ.get('REDIS_SERVICE_PORT', DEFAULT_REDIS_SERVICE_PORT), +# 'REDIS_DATABASE_ID' : os.environ.get('REDIS_DATABASE_ID', DEFAULT_REDIS_DATABASE_ID ), +#} -SCENARIOS = [ - ('all_sqlalchemy', {}, MessageBrokerBackendEnum.INMEMORY, {} ), - ('all_inmemory', DatabaseBackendEnum.INMEMORY, {}, MessageBrokerBackendEnum.INMEMORY, {} ) +#SCENARIOS = [ +# ('db:cockroach_mb:inmemory', None, {}, None, {}), +# ('all_inmemory', DatabaseBackendEnum.INMEMORY, {}, MessageBrokerBackendEnum.INMEMORY, {} ) # ('all_redis', DatabaseBackendEnum.REDIS, REDIS_CONFIG, MessageBrokerBackendEnum.REDIS, REDIS_CONFIG), -] +#] -@pytest.fixture(scope='session', ids=[str(scenario[0]) for scenario in SCENARIOS], params=SCENARIOS) -def context_s_mb(request) -> Tuple[Session, MessageBroker]: - name,db_session,mb_backend,mb_settings = request.param - msg = 'Running scenario {:s} db_session={:s}, mb_backend={:s}, mb_settings={:s}...' - LOGGER.info(msg.format(str(name), str(db_session), str(mb_backend.value), str(mb_settings))) - - db_uri = 'cockroachdb://root@10.152.183.111:26257/defaultdb?sslmode=disable' - LOGGER.debug('Connecting to DB: {}'.format(db_uri)) - - try: - engine = create_engine(db_uri) - except Exception as e: - LOGGER.error("Failed to connect to database.") - LOGGER.error(f"{e}") - return 1 +#@pytest.fixture(scope='session', ids=[str(scenario[0]) for scenario in SCENARIOS], params=SCENARIOS) +@pytest.fixture(scope='session') +def context_db_mb(request) -> Tuple[Session, MessageBroker]: + #name,db_session,mb_backend,mb_settings = request.param + #msg = 'Running scenario {:s} db_session={:s}, mb_backend={:s}, mb_settings={:s}...' + #LOGGER.info(msg.format(str(name), str(db_session), str(mb_backend.value), str(mb_settings))) - Base.metadata.create_all(engine) - _session = sessionmaker(bind=engine, expire_on_commit=False) + _db_engine = Engine().get_engine() - _message_broker = MessageBroker(get_messagebroker_backend(backend=mb_backend, **mb_settings)) - yield _session, _message_broker - _message_broker.terminate() + _msg_broker = MessageBroker(get_messagebroker_backend(backend=MessageBrokerBackendEnum.INMEMORY)) + yield _db_engine, _msg_broker + _msg_broker.terminate() @pytest.fixture(scope='session') -def context_service_grpc(context_s_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name - _service = ContextService(context_s_mb[0], context_s_mb[1]) +def context_service_grpc(context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name + _service = ContextService(context_db_mb[0], context_db_mb[1]) _service.start() yield _service _service.stop() -@pytest.fixture(scope='session') -def context_service_rest(context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name - database = context_db_mb[0] - _rest_server = RestServer() - for endpoint_name, resource_class, resource_url in RESOURCES: - _rest_server.add_resource(resource_class, resource_url, endpoint=endpoint_name, resource_class_args=(database,)) - _rest_server.start() - time.sleep(1) # bring time for the server to start - yield _rest_server - _rest_server.shutdown() - _rest_server.join() + +#@pytest.fixture(scope='session') +#def context_service_rest(context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name +# database = context_db_mb[0] +# _rest_server = RestServer() +# for endpoint_name, resource_class, resource_url in RESOURCES: +# _rest_server.add_resource(resource_class, resource_url, endpoint=endpoint_name, resource_class_args=(database,)) +# _rest_server.start() +# time.sleep(1) # bring time for the server to start +# yield _rest_server +# _rest_server.shutdown() +# _rest_server.join() + @pytest.fixture(scope='session') def context_client_grpc(context_service_grpc : ContextService): # pylint: disable=redefined-outer-name _client = ContextClient() yield _client _client.close() -""" -def do_rest_request(url : str): - base_url = get_service_baseurl_http(ServiceNameEnum.CONTEXT) - request_url = 'http://{:s}:{:s}{:s}{:s}'.format(str(LOCAL_HOST), str(HTTP_PORT), str(base_url), url) - LOGGER.warning('Request: GET {:s}'.format(str(request_url))) - reply = requests.get(request_url) - LOGGER.warning('Reply: {:s}'.format(str(reply.text))) - assert reply.status_code == 200, 'Reply failed with code {}'.format(reply.status_code) - return reply.json() -""" -"""# ----- Test gRPC methods ---------------------------------------------------------------------------------------------- -def test_grpc_context( - context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name - context_s_mb : Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name - Session = context_s_mb[0] +#def do_rest_request(url : str): +# base_url = get_service_baseurl_http(ServiceNameEnum.CONTEXT) +# request_url = 'http://{:s}:{:s}{:s}{:s}'.format(str(LOCAL_HOST), str(HTTP_PORT), str(base_url), url) +# LOGGER.warning('Request: GET {:s}'.format(str(request_url))) +# reply = requests.get(request_url) +# LOGGER.warning('Reply: {:s}'.format(str(reply.text))) +# assert reply.status_code == 200, 'Reply failed with code {}'.format(reply.status_code) +# return reply.json() - database = Database(Session) +# ----- Test gRPC methods ---------------------------------------------------------------------------------------------- + +def test_grpc_context( + context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name + context_db_mb : Tuple[sqlalchemy.engine.Engine, MessageBroker] # pylint: disable=redefined-outer-name +) -> None: + db_engine = context_db_mb[0] # ----- Clean the database ----------------------------------------------------------------------------------------- - database.clear() + rebuild_database(db_engine, drop_if_exists=True) + # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- - events_collector = EventsCollector(context_client_grpc) + events_collector = EventsCollector( + context_client_grpc, log_events_received=True, + activate_context_collector = True, activate_topology_collector = False, activate_device_collector = False, + activate_link_collector = False, activate_service_collector = False, activate_slice_collector = False, + activate_connection_collector = False) events_collector.start() # ----- Get when the object does not exist ------------------------------------------------------------------------- @@ -163,71 +166,95 @@ def test_grpc_context( assert len(response.contexts) == 0 # ----- Dump state of database before create the object ------------------------------------------------------------ - db_entries = database.dump_all() - LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - for db_entry in db_entries: - LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover - LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 0 + #db_entries = database.dump_all() + #LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + #for db_entry in db_entries: + # LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover + #LOGGER.info('-----------------------------------------------------------') + #assert len(db_entries) == 0 # ----- Create the object ------------------------------------------------------------------------------------------ response = context_client_grpc.SetContext(Context(**CONTEXT)) assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID - wrong_uuid = 'c97c4185-e1d1-4ea7-b6b9-afbf76cb61f4' + wrong_context_uuid = str(uuid.uuid4()) + wrong_context_id = json_context_id(wrong_context_uuid) with pytest.raises(grpc.RpcError) as e: - WRONG_TOPOLOGY_ID = copy.deepcopy(TOPOLOGY_ID) - WRONG_TOPOLOGY_ID['context_id']['context_uuid']['uuid'] = wrong_uuid WRONG_CONTEXT = copy.deepcopy(CONTEXT) - WRONG_CONTEXT['topology_ids'].append(WRONG_TOPOLOGY_ID) + WRONG_CONTEXT['topology_ids'].append(json_topology_id(str(uuid.uuid4()), context_id=wrong_context_id)) context_client_grpc.SetContext(Context(**WRONG_CONTEXT)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT msg = 'request.topology_ids[0].context_id.context_uuid.uuid({}) is invalid; '\ - 'should be == request.context_id.context_uuid.uuid({})'.format(wrong_uuid, DEFAULT_CONTEXT_UUID) + 'should be == request.context_id.context_uuid.uuid({})'.format(wrong_context_uuid, DEFAULT_CONTEXT_UUID) assert e.value.details() == msg with pytest.raises(grpc.RpcError) as e: - WRONG_SERVICE_ID = copy.deepcopy(SERVICE_R1_R2_ID) - WRONG_SERVICE_ID['context_id']['context_uuid']['uuid'] = wrong_uuid WRONG_CONTEXT = copy.deepcopy(CONTEXT) - WRONG_CONTEXT['service_ids'].append(WRONG_SERVICE_ID) + WRONG_CONTEXT['service_ids'].append(json_service_id(str(uuid.uuid4()), context_id=wrong_context_id)) context_client_grpc.SetContext(Context(**WRONG_CONTEXT)) assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT msg = 'request.service_ids[0].context_id.context_uuid.uuid({}) is invalid; '\ - 'should be == request.context_id.context_uuid.uuid({})'.format(wrong_uuid, DEFAULT_CONTEXT_UUID) + 'should be == request.context_id.context_uuid.uuid({})'.format(wrong_context_uuid, DEFAULT_CONTEXT_UUID) + assert e.value.details() == msg + + with pytest.raises(grpc.RpcError) as e: + WRONG_CONTEXT = copy.deepcopy(CONTEXT) + WRONG_CONTEXT['slice_ids'].append(json_slice_id(str(uuid.uuid4()), context_id=wrong_context_id)) + context_client_grpc.SetContext(Context(**WRONG_CONTEXT)) + assert e.value.code() == grpc.StatusCode.INVALID_ARGUMENT + msg = 'request.slice_ids[0].context_id.context_uuid.uuid({}) is invalid; '\ + 'should be == request.context_id.context_uuid.uuid({})'.format(wrong_context_uuid, DEFAULT_CONTEXT_UUID) assert e.value.details() == msg # ----- Check create event ----------------------------------------------------------------------------------------- - event = events_collector.get_event(block=True) + event = events_collector.get_event(block=True, timeout=10.0) assert isinstance(event, ContextEvent) - assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE - assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + #assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE + #assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- Get when the object exists --------------------------------------------------------------------------------- + response = context_client_grpc.GetContext(ContextId(**CONTEXT_ID)) + assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.name == '' + assert len(response.topology_ids) == 0 + assert len(response.service_ids) == 0 + assert len(response.slice_ids) == 0 + + # ----- List when the object exists -------------------------------------------------------------------------------- + response = context_client_grpc.ListContextIds(Empty()) + assert len(response.context_ids) == 1 + assert response.context_ids[0].context_uuid.uuid == DEFAULT_CONTEXT_UUID + + response = context_client_grpc.ListContexts(Empty()) + assert len(response.contexts) == 1 + assert response.contexts[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.contexts[0].name == '' + assert len(response.contexts[0].topology_ids) == 0 + assert len(response.contexts[0].service_ids) == 0 + assert len(response.contexts[0].slice_ids) == 0 + # ----- Update the object ------------------------------------------------------------------------------------------ - response = context_client_grpc.SetContext(Context(**CONTEXT)) + new_context_name = 'new' + CONTEXT_WITH_NAME = copy.deepcopy(CONTEXT) + CONTEXT_WITH_NAME['name'] = new_context_name + response = context_client_grpc.SetContext(Context(**CONTEXT_WITH_NAME)) assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Check update event ----------------------------------------------------------------------------------------- - event = events_collector.get_event(block=True) + event = events_collector.get_event(block=True, timeout=10.0) assert isinstance(event, ContextEvent) - assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE - assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - - # ----- Dump state of database after create/update the object ------------------------------------------------------ - db_entries = database.dump_all() - - LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - for db_entry in db_entries: - LOGGER.info(db_entry) - LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 1 + #assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE + #assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID - # ----- Get when the object exists --------------------------------------------------------------------------------- + # ----- Get when the object is modified ---------------------------------------------------------------------------- response = context_client_grpc.GetContext(ContextId(**CONTEXT_ID)) assert response.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.name == new_context_name assert len(response.topology_ids) == 0 assert len(response.service_ids) == 0 + assert len(response.slice_ids) == 0 - # ----- List when the object exists -------------------------------------------------------------------------------- + # ----- List when the object is modified --------------------------------------------------------------------------- response = context_client_grpc.ListContextIds(Empty()) assert len(response.context_ids) == 1 assert response.context_ids[0].context_uuid.uuid == DEFAULT_CONTEXT_UUID @@ -235,35 +262,53 @@ def test_grpc_context( response = context_client_grpc.ListContexts(Empty()) assert len(response.contexts) == 1 assert response.contexts[0].context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + assert response.contexts[0].name == new_context_name assert len(response.contexts[0].topology_ids) == 0 assert len(response.contexts[0].service_ids) == 0 + assert len(response.contexts[0].slice_ids) == 0 + + # ----- Dump state of database after create/update the object ------------------------------------------------------ + #db_entries = database.dump_all() + #LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + #for db_entry in db_entries: + # LOGGER.info(db_entry) + #LOGGER.info('-----------------------------------------------------------') + #assert len(db_entries) == 1 # ----- Remove the object ------------------------------------------------------------------------------------------ context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) # ----- Check remove event ----------------------------------------------------------------------------------------- - # event = events_collector.get_event(block=True) - # assert isinstance(event, ContextEvent) - # assert event.event.event_type == EventTypeEnum.EVENTTYPE_REMOVE - # assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + event = events_collector.get_event(block=True, timeout=10.0) + assert isinstance(event, ContextEvent) + #assert event.event.event_type == EventTypeEnum.EVENTTYPE_REMOVE + #assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID + + # ----- List after deleting the object ----------------------------------------------------------------------------- + response = context_client_grpc.ListContextIds(Empty()) + assert len(response.context_ids) == 0 + + response = context_client_grpc.ListContexts(Empty()) + assert len(response.contexts) == 0 # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- events_collector.stop() # ----- Dump state of database after remove the object ------------------------------------------------------------- - db_entries = database.dump_all() - - LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) - for db_entry in db_entries: - LOGGER.info(db_entry) - LOGGER.info('-----------------------------------------------------------') - assert len(db_entries) == 0 + #db_entries = database.dump_all() + #LOGGER.info('----- Database Dump [{:3d} entries] -------------------------'.format(len(db_entries))) + #for db_entry in db_entries: + # LOGGER.info(db_entry) + #LOGGER.info('-----------------------------------------------------------') + #assert len(db_entries) == 0 + raise Exception() +""" def test_grpc_topology( context_client_grpc: ContextClient, # pylint: disable=redefined-outer-name - context_s_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name - session = context_s_mb[0] + context_db_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name + session = context_db_mb[0] database = Database(session) @@ -394,8 +439,8 @@ def test_grpc_topology( def test_grpc_device( context_client_grpc: ContextClient, # pylint: disable=redefined-outer-name - context_s_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name - session = context_s_mb[0] + context_db_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name + session = context_db_mb[0] database = Database(session) @@ -571,8 +616,8 @@ def test_grpc_device( def test_grpc_link( context_client_grpc: ContextClient, # pylint: disable=redefined-outer-name - context_s_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name - session = context_s_mb[0] + context_db_mb: Tuple[Session, MessageBroker]): # pylint: disable=redefined-outer-name + session = context_db_mb[0] database = Database(session) @@ -753,10 +798,11 @@ def test_grpc_link( assert len(db_entries) == 0 """ +""" def test_grpc_service( context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name - context_s_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name - Session = context_s_mb[0] + context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name + Session = context_db_mb[0] # ----- Clean the database ----------------------------------------------------------------------------------------- database = Database(Session) database.clear() @@ -941,14 +987,13 @@ def test_grpc_service( LOGGER.info(' [{:>4s}] {:40s} :: {:s}'.format(*db_entry)) # pragma: no cover LOGGER.info('-----------------------------------------------------------') assert len(db_entries) == 0 - - """ +""" def test_grpc_connection( context_client_grpc : ContextClient, # pylint: disable=redefined-outer-name context_db_mb : Tuple[Database, MessageBroker]): # pylint: disable=redefined-outer-name - Session = context_s_mb[0] + Session = context_db_mb[0] database = Database(Session) -- GitLab From c71777a4b7f5a1444c321ba70946029a8a2141a9 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 17 Dec 2022 15:25:44 +0000 Subject: [PATCH 086/353] Device Driver Transport API: - Improved endpoint type definition --- src/device/service/drivers/transport_api/Tools.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/device/service/drivers/transport_api/Tools.py b/src/device/service/drivers/transport_api/Tools.py index 6ae928eb8..898929419 100644 --- a/src/device/service/drivers/transport_api/Tools.py +++ b/src/device/service/drivers/transport_api/Tools.py @@ -47,7 +47,15 @@ def config_getter(root_url, resource_key, timeout): elif 'context' in context: context = context['context'] for sip in context['service-interface-point']: - endpoint_type = sip.get('layer-protocol-name', '10Gbps') + layer_protocol_name = sip.get('layer-protocol-name', '?') + supportable_spectrum = sip.get('tapi-photonic-media:media-channel-service-interface-point-spec', {}) + supportable_spectrum = supportable_spectrum.get('mc-pool', {}) + supportable_spectrum = supportable_spectrum.get('supportable-spectrum', []) + supportable_spectrum = supportable_spectrum[0] if len(supportable_spectrum) == 1 else {} + grid_type = supportable_spectrum.get('frequency-constraint', {}).get('grid-type') + granularity = supportable_spectrum.get('frequency-constraint', {}).get('adjustment-granularity') + direction = sip.get('direction', '?') + endpoint_type = ':'.join([layer_protocol_name, grid_type, granularity, direction]) endpoint_url = '/endpoints/endpoint[{:s}]'.format(sip['uuid']) endpoint_data = {'uuid': sip['uuid'], 'type': endpoint_type} result.append((endpoint_url, endpoint_data)) -- GitLab From 1844c6089da39d4dcedf493b3627292fa5e3252a Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 17 Dec 2022 15:27:27 +0000 Subject: [PATCH 087/353] Tool load-gen: - added support for L3NM and TAPI services --- src/tests/tools/load_gen/Constants.py | 17 ++ src/tests/tools/load_gen/Parameters.py | 11 +- src/tests/tools/load_gen/ServiceGenerator.py | 209 +++++++++++++++---- src/tests/tools/load_gen/__main__.py | 16 +- 4 files changed, 204 insertions(+), 49 deletions(-) create mode 100644 src/tests/tools/load_gen/Constants.py diff --git a/src/tests/tools/load_gen/Constants.py b/src/tests/tools/load_gen/Constants.py new file mode 100644 index 000000000..28c1c65be --- /dev/null +++ b/src/tests/tools/load_gen/Constants.py @@ -0,0 +1,17 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +SERVICE_TYPE_L2NM = 'l2nm' +SERVICE_TYPE_L3NM = 'l3nm' +SERVICE_TYPE_TAPI = 'tapi' diff --git a/src/tests/tools/load_gen/Parameters.py b/src/tests/tools/load_gen/Parameters.py index f3fe742df..fa2ac01f3 100644 --- a/src/tests/tools/load_gen/Parameters.py +++ b/src/tests/tools/load_gen/Parameters.py @@ -12,14 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional +from typing import List, Optional class Parameters: def __init__( - self, num_services : int, offered_load : Optional[float] = None, inter_arrival_time : Optional[float] = None, - holding_time : Optional[float] = None, dry_mode : bool = False + self, num_services : int, service_types : List[str], offered_load : Optional[float] = None, + inter_arrival_time : Optional[float] = None, holding_time : Optional[float] = None, + dry_mode : bool = False ) -> None: self._num_services = num_services + self._service_types = service_types self._offered_load = offered_load self._inter_arrival_time = inter_arrival_time self._holding_time = holding_time @@ -35,6 +37,9 @@ class Parameters: @property def num_services(self): return self._num_services + @property + def service_types(self): return self._service_types + @property def offered_load(self): return self._offered_load diff --git a/src/tests/tools/load_gen/ServiceGenerator.py b/src/tests/tools/load_gen/ServiceGenerator.py index d21c345a3..632225171 100644 --- a/src/tests/tools/load_gen/ServiceGenerator.py +++ b/src/tests/tools/load_gen/ServiceGenerator.py @@ -19,17 +19,28 @@ from common.tools.object_factory.Constraint import json_constraint_custom from common.tools.object_factory.ConfigRule import json_config_rule_set from common.tools.object_factory.Device import json_device_id from common.tools.object_factory.EndPoint import json_endpoint_id -from common.tools.object_factory.Service import json_service_l2nm_planned +from common.tools.object_factory.Service import ( + json_service_l2nm_planned, json_service_l3nm_planned, json_service_tapi_planned) from context.client.ContextClient import ContextClient +from .Constants import SERVICE_TYPE_L2NM, SERVICE_TYPE_L3NM, SERVICE_TYPE_TAPI +from .Parameters import Parameters LOGGER = logging.getLogger(__name__) +ENDPOINT_COMPATIBILITY = { + 'PHOTONIC_MEDIA:FLEX:G_6_25GHZ:INPUT': 'PHOTONIC_MEDIA:FLEX:G_6_25GHZ:OUTPUT', + 'PHOTONIC_MEDIA:DWDM:G_50GHZ:INPUT' : 'PHOTONIC_MEDIA:DWDM:G_50GHZ:OUTPUT', +} + class ServiceGenerator: - def __init__(self) -> None: + def __init__(self, parameters : Parameters) -> None: + self._parameters = parameters self._lock = threading.Lock() self._num_services = 0 self._available_device_endpoints : Dict[str, Set[str]] = dict() self._used_device_endpoints : Dict[str, Dict[str, str]] = dict() + self._endpoint_ids_to_types : Dict[Tuple[str, str], str] = dict() + self._endpoint_types_to_ids : Dict[str, Set[Tuple[str, str]]] = dict() def initialize(self) -> None: with self._lock: @@ -44,7 +55,10 @@ class ServiceGenerator: _endpoints = self._available_device_endpoints.setdefault(device_uuid, set()) for endpoint in device.device_endpoints: endpoint_uuid = endpoint.endpoint_id.endpoint_uuid.uuid + endpoint_type = endpoint.endpoint_type _endpoints.add(endpoint_uuid) + self._endpoint_ids_to_types.setdefault((device_uuid, endpoint_uuid), endpoint_type) + self._endpoint_types_to_ids.setdefault(endpoint_type, set()).add((device_uuid, endpoint_uuid)) links = context_client.ListLinks(Empty()) for link in links.links: @@ -55,6 +69,15 @@ class ServiceGenerator: _endpoints.discard(endpoint_uuid) if len(_endpoints) == 0: self._available_device_endpoints.pop(device_uuid, None) + endpoint_type = self._endpoint_ids_to_types.pop((device_uuid, endpoint_uuid), None) + if endpoint_type is None: continue + + if endpoint_type not in self._endpoint_types_to_ids: continue + endpoints_for_type = self._endpoint_types_to_ids[endpoint_type] + endpoint_key = (device_uuid, endpoint_uuid) + if endpoint_key not in endpoints_for_type: continue + endpoints_for_type.discard(endpoint_key) + @property def num_services_generated(self): return self._num_services @@ -68,15 +91,42 @@ class ServiceGenerator: LOGGER.info('[dump_state] used_device_endpoints = {:s}'.format(json.dumps(self._used_device_endpoints))) def _use_device_endpoint( - self, service_uuid : str, exclude_device_uuids : Set[str] = set() + self, service_uuid : str, endpoint_types : Optional[Set[str]] = None, exclude_device_uuids : Set[str] = set() ) -> Optional[Tuple[str, str]]: with self._lock: - elegible_device_endpoints = { - device_uuid:device_endpoint_uuids - for device_uuid,device_endpoint_uuids in self._available_device_endpoints.items() - if device_uuid not in exclude_device_uuids and len(device_endpoint_uuids) > 0 - } - if len(elegible_device_endpoints) == 0: return None + compatible_endpoints : Set[Tuple[str, str]] = set() + elegible_device_endpoints : Dict[str, Set[str]] = {} + + if endpoint_types is None: + # allow all + elegible_device_endpoints : Dict[str, Set[str]] = { + device_uuid:device_endpoint_uuids + for device_uuid,device_endpoint_uuids in self._available_device_endpoints.items() + if device_uuid not in exclude_device_uuids and len(device_endpoint_uuids) > 0 + } + else: + # allow only compatible endpoints + for endpoint_type in endpoint_types: + if endpoint_type not in self._endpoint_types_to_ids: continue + compatible_endpoints.update(self._endpoint_types_to_ids[endpoint_type]) + + for device_uuid,device_endpoint_uuids in self._available_device_endpoints.items(): + if device_uuid in exclude_device_uuids or len(device_endpoint_uuids) == 0: continue + for endpoint_uuid in device_endpoint_uuids: + endpoint_key = (device_uuid,endpoint_uuid) + if endpoint_key not in compatible_endpoints: continue + elegible_device_endpoints.setdefault(device_uuid, set()).add(endpoint_uuid) + + if len(elegible_device_endpoints) == 0: + LOGGER.warning(' '.join([ + '>> No endpoint is available:', + 'endpoint_types={:s}'.format(str(endpoint_types)), + 'exclude_device_uuids={:s}'.format(str(exclude_device_uuids)), + 'self._endpoint_types_to_ids={:s}'.format(str(self._endpoint_types_to_ids)), + 'self._available_device_endpoints={:s}'.format(str(self._available_device_endpoints)), + 'compatible_endpoints={:s}'.format(str(compatible_endpoints)), + ])) + return None device_uuid = random.choice(list(elegible_device_endpoints.keys())) device_endpoint_uuids = elegible_device_endpoints.get(device_uuid) endpoint_uuid = random.choice(list(device_endpoint_uuids)) @@ -95,47 +145,124 @@ class ServiceGenerator: num_service = self._num_services #service_uuid = str(uuid.uuid4()) service_uuid = 'svc_{:d}'.format(num_service) - src = self._use_device_endpoint(service_uuid) - if src is None: return None + + # choose service type + service_type = random.choice(self._parameters.service_types) + + # choose source endpoint + src_endpoint_types = set(ENDPOINT_COMPATIBILITY.keys()) if service_type in {SERVICE_TYPE_TAPI} else None + src = self._use_device_endpoint(service_uuid, endpoint_types=src_endpoint_types) + if src is None: + LOGGER.warning('>> No source endpoint is available') + return None src_device_uuid,src_endpoint_uuid = src - dst = self._use_device_endpoint(service_uuid, exclude_device_uuids={src_device_uuid}) + + # identify compatible destination endpoint types + src_endpoint_type = self._endpoint_ids_to_types.get((src_device_uuid,src_endpoint_uuid)) + dst_endpoint_type = ENDPOINT_COMPATIBILITY.get(src_endpoint_type) + + # identify expluded destination devices + exclude_device_uuids = {} if service_type in {SERVICE_TYPE_TAPI} else {src_device_uuid} + + # choose feasible destination endpoint + dst = self._use_device_endpoint( + service_uuid, endpoint_types={dst_endpoint_type}, exclude_device_uuids=exclude_device_uuids) + + # if destination endpoint not found, release source, and terminate current service generation if dst is None: + LOGGER.warning('>> No destination endpoint is available') self._release_device_endpoint(src_device_uuid, src_endpoint_uuid) return None + + # compose endpoints dst_device_uuid,dst_endpoint_uuid = dst endpoint_ids = [ json_endpoint_id(json_device_id(src_device_uuid), src_endpoint_uuid), json_endpoint_id(json_device_id(dst_device_uuid), dst_endpoint_uuid), ] - constraints = [ - json_constraint_custom('bandwidth[gbps]', '10.0'), - json_constraint_custom('latency[ms]', '20.0'), - ] - vlan_id = num_service % 1000 - circuit_id = '{:03d}'.format(vlan_id) - src_router_id = '.'.join([src_device_uuid.replace('R', ''), '0'] + src_endpoint_uuid.split('/')) - dst_router_id = '.'.join([dst_device_uuid.replace('R', ''), '0'] + dst_endpoint_uuid.split('/')) - config_rules = [ - json_config_rule_set('/settings', { - 'mtu': 1512 - }), - json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(src_device_uuid, src_endpoint_uuid), { - 'router_id': src_router_id, - 'sub_interface_index': vlan_id, - 'vlan_id': vlan_id, - 'remote_router': dst_router_id, - 'circuit_id': circuit_id, - }), - json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(dst_device_uuid, dst_endpoint_uuid), { - 'router_id': dst_router_id, - 'sub_interface_index': vlan_id, - 'vlan_id': vlan_id, - 'remote_router': src_router_id, - 'circuit_id': circuit_id, - }), - ] - return json_service_l2nm_planned( - service_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules) + + if service_type == SERVICE_TYPE_L2NM: + constraints = [ + json_constraint_custom('bandwidth[gbps]', '10.0'), + json_constraint_custom('latency[ms]', '20.0'), + ] + vlan_id = num_service % 1000 + circuit_id = '{:03d}'.format(vlan_id) + src_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', ''))) + dst_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', ''))) + config_rules = [ + json_config_rule_set('/settings', { + 'mtu': 1512 + }), + json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(src_device_uuid, src_endpoint_uuid), { + 'router_id': src_router_id, + 'sub_interface_index': vlan_id, + 'vlan_id': vlan_id, + 'remote_router': dst_router_id, + 'circuit_id': circuit_id, + }), + json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(dst_device_uuid, dst_endpoint_uuid), { + 'router_id': dst_router_id, + 'sub_interface_index': vlan_id, + 'vlan_id': vlan_id, + 'remote_router': src_router_id, + 'circuit_id': circuit_id, + }), + ] + return json_service_l2nm_planned( + service_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules) + + elif service_type == SERVICE_TYPE_L3NM: + constraints = [ + json_constraint_custom('bandwidth[gbps]', '10.0'), + json_constraint_custom('latency[ms]', '20.0'), + ] + vlan_id = num_service % 1000 + bgp_as = 60000 + (num_service % 10000) + bgp_route_target = '{:5d}:{:03d}'.format(bgp_as, 333) + route_distinguisher = '{:5d}:{:03d}'.format(bgp_as, vlan_id) + src_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', ''))) + dst_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', ''))) + src_address_ip = '.'.join([src_device_uuid.replace('R', ''), '0'] + src_endpoint_uuid.split('/')) + dst_address_ip = '.'.join([dst_device_uuid.replace('R', ''), '0'] + dst_endpoint_uuid.split('/')) + config_rules = [ + json_config_rule_set('/settings', { + 'mtu' : 1512, + 'bgp_as' : bgp_as, + 'bgp_route_target': bgp_route_target, + }), + json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(src_device_uuid, src_endpoint_uuid), { + 'router_id' : src_router_id, + 'route_distinguisher': route_distinguisher, + 'sub_interface_index': vlan_id, + 'vlan_id' : vlan_id, + 'address_ip' : src_address_ip, + 'address_prefix' : 16, + }), + json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(dst_device_uuid, dst_endpoint_uuid), { + 'router_id' : dst_router_id, + 'route_distinguisher': route_distinguisher, + 'sub_interface_index': vlan_id, + 'vlan_id' : vlan_id, + 'address_ip' : dst_address_ip, + 'address_prefix' : 16, + }), + ] + return json_service_l3nm_planned( + service_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules) + + elif service_type == SERVICE_TYPE_TAPI: + config_rules = [ + json_config_rule_set('/settings', { + 'capacity_value' : 50.0, + 'capacity_unit' : 'GHz', + 'layer_proto_name': 'PHOTONIC_MEDIA', + 'layer_proto_qual': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC', + 'direction' : 'UNIDIRECTIONAL', + }), + ] + return json_service_tapi_planned( + service_uuid, endpoint_ids=endpoint_ids, constraints=[], config_rules=config_rules) def release_service(self, json_service : Dict) -> None: for endpoint_id in json_service['service_endpoint_ids']: diff --git a/src/tests/tools/load_gen/__main__.py b/src/tests/tools/load_gen/__main__.py index 7a81dbcba..28ffb2a9a 100644 --- a/src/tests/tools/load_gen/__main__.py +++ b/src/tests/tools/load_gen/__main__.py @@ -13,6 +13,7 @@ # limitations under the License. import logging, sys +from .Constants import SERVICE_TYPE_L2NM, SERVICE_TYPE_L3NM, SERVICE_TYPE_TAPI from .Parameters import Parameters from .ServiceGenerator import ServiceGenerator from .ServiceScheduler import ServiceScheduler @@ -23,14 +24,19 @@ LOGGER = logging.getLogger(__name__) def main(): LOGGER.info('Starting...') parameters = Parameters( - num_services = 100, - offered_load = 50, - holding_time = 10, - dry_mode = False, # in dry mode, no request is sent to TeraFlowSDN + num_services = 100, + service_types = [ + #SERVICE_TYPE_L2NM, + #SERVICE_TYPE_L3NM, + SERVICE_TYPE_TAPI, + ], + offered_load = 50, + holding_time = 10, + dry_mode = False, # in dry mode, no request is sent to TeraFlowSDN ) LOGGER.info('Initializing Generator...') - service_generator = ServiceGenerator() + service_generator = ServiceGenerator(parameters) service_generator.initialize() LOGGER.info('Running Schedule...') -- GitLab From ba7a86657ecffbabde7bbb7a5ccd479e0975491c Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 17 Dec 2022 23:25:31 +0000 Subject: [PATCH 088/353] Manifests: - added missing metrics ports in core services - updated scalping time to 5 seconds --- manifests/contextservice.yaml | 30 +++++++++++++++--------------- manifests/dltservice.yaml | 7 +++++++ manifests/interdomainservice.yaml | 7 +++++++ manifests/monitoringservice.yaml | 9 +++++++++ manifests/pathcompservice.yaml | 7 +++++++ manifests/servicemonitors.yaml | 16 ++++++++-------- 6 files changed, 53 insertions(+), 23 deletions(-) diff --git a/manifests/contextservice.yaml b/manifests/contextservice.yaml index 299864032..80d6c94f9 100644 --- a/manifests/contextservice.yaml +++ b/manifests/contextservice.yaml @@ -28,17 +28,17 @@ spec: spec: terminationGracePeriodSeconds: 5 containers: - - name: redis - image: redis:6.2 - ports: - - containerPort: 6379 - resources: - requests: - cpu: 100m - memory: 128Mi - limits: - cpu: 500m - memory: 1024Mi + #- name: redis + # image: redis:6.2 + # ports: + # - containerPort: 6379 + # resources: + # requests: + # cpu: 100m + # memory: 128Mi + # limits: + # cpu: 500m + # memory: 1024Mi - name: server image: registry.gitlab.com/teraflow-h2020/controller/context:latest imagePullPolicy: Always @@ -48,11 +48,11 @@ spec: - containerPort: 9192 env: - name: DB_BACKEND - value: "redis" + value: "inmemory" - name: MB_BACKEND - value: "redis" - - name: REDIS_DATABASE_ID - value: "0" + value: "inmemory" + #- name: REDIS_DATABASE_ID + # value: "0" - name: LOG_LEVEL value: "INFO" - name: POPULATE_FAKE_DATA diff --git a/manifests/dltservice.yaml b/manifests/dltservice.yaml index d2ad4f404..0f6b5bb9d 100644 --- a/manifests/dltservice.yaml +++ b/manifests/dltservice.yaml @@ -32,6 +32,7 @@ spec: imagePullPolicy: Always ports: - containerPort: 8080 + - containerPort: 9192 env: - name: LOG_LEVEL value: "INFO" @@ -82,6 +83,8 @@ apiVersion: v1 kind: Service metadata: name: dltservice + labels: + app: dltservice spec: type: ClusterIP selector: @@ -91,3 +94,7 @@ spec: protocol: TCP port: 8080 targetPort: 8080 + - name: metrics + protocol: TCP + port: 9192 + targetPort: 9192 diff --git a/manifests/interdomainservice.yaml b/manifests/interdomainservice.yaml index 3ef3ffba3..b275035f6 100644 --- a/manifests/interdomainservice.yaml +++ b/manifests/interdomainservice.yaml @@ -32,6 +32,7 @@ spec: imagePullPolicy: Always ports: - containerPort: 10010 + - containerPort: 9192 env: - name: LOG_LEVEL value: "INFO" @@ -53,6 +54,8 @@ apiVersion: v1 kind: Service metadata: name: interdomainservice + labels: + app: interdomainservice spec: type: ClusterIP selector: @@ -62,3 +65,7 @@ spec: protocol: TCP port: 10010 targetPort: 10010 + - name: metrics + protocol: TCP + port: 9192 + targetPort: 9192 diff --git a/manifests/monitoringservice.yaml b/manifests/monitoringservice.yaml index 39acfd523..aed8d1c51 100644 --- a/manifests/monitoringservice.yaml +++ b/manifests/monitoringservice.yaml @@ -72,6 +72,9 @@ spec: - name: grpc containerPort: 7070 protocol: TCP + - name: metrics + containerPort: 9192 + protocol: TCP env: - name: LOG_LEVEL value: "INFO" @@ -101,6 +104,8 @@ apiVersion: v1 kind: Service metadata: name: monitoringservice + labels: + app: monitoringservice spec: type: ClusterIP selector: @@ -122,6 +127,10 @@ spec: protocol: TCP port: 8812 targetPort: 8812 + - name: metrics + protocol: TCP + port: 9192 + targetPort: 9192 --- apiVersion: networking.k8s.io/v1 diff --git a/manifests/pathcompservice.yaml b/manifests/pathcompservice.yaml index 51c83a0f3..71c927b56 100644 --- a/manifests/pathcompservice.yaml +++ b/manifests/pathcompservice.yaml @@ -33,6 +33,7 @@ spec: imagePullPolicy: Always ports: - containerPort: 10020 + - containerPort: 9192 env: - name: LOG_LEVEL value: "INFO" @@ -76,6 +77,8 @@ apiVersion: v1 kind: Service metadata: name: pathcompservice + labels: + app: pathcompservice spec: type: ClusterIP selector: @@ -89,3 +92,7 @@ spec: protocol: TCP port: 8081 targetPort: 8081 + - name: metrics + protocol: TCP + port: 9192 + targetPort: 9192 diff --git a/manifests/servicemonitors.yaml b/manifests/servicemonitors.yaml index 492e1ac60..ad5f042ba 100644 --- a/manifests/servicemonitors.yaml +++ b/manifests/servicemonitors.yaml @@ -21,7 +21,7 @@ spec: - port: metrics # named port in target app scheme: http path: /metrics # path to scrape - interval: 1s # scrape interval + interval: 5s # scrape interval namespaceSelector: any: false matchNames: @@ -50,7 +50,7 @@ spec: - port: metrics # named port in target app scheme: http path: /metrics # path to scrape - interval: 1s # scrape interval + interval: 5s # scrape interval namespaceSelector: any: false matchNames: @@ -79,7 +79,7 @@ spec: - port: metrics # named port in target app scheme: http path: /metrics # path to scrape - interval: 1s # scrape interval + interval: 5s # scrape interval namespaceSelector: any: false matchNames: @@ -108,7 +108,7 @@ spec: - port: metrics # named port in target app scheme: http path: /metrics # path to scrape - interval: 1s # scrape interval + interval: 5s # scrape interval namespaceSelector: any: false matchNames: @@ -137,7 +137,7 @@ spec: - port: metrics # named port in target app scheme: http path: /metrics # path to scrape - interval: 1s # scrape interval + interval: 5s # scrape interval namespaceSelector: any: false matchNames: @@ -166,7 +166,7 @@ spec: - port: metrics # named port in target app scheme: http path: /metrics # path to scrape - interval: 1s # scrape interval + interval: 5s # scrape interval namespaceSelector: any: false matchNames: @@ -195,7 +195,7 @@ spec: - port: metrics # named port in target app scheme: http path: /metrics # path to scrape - interval: 1s # scrape interval + interval: 5s # scrape interval namespaceSelector: any: false matchNames: @@ -224,7 +224,7 @@ spec: - port: metrics # named port in target app scheme: http path: /metrics # path to scrape - interval: 1s # scrape interval + interval: 5s # scrape interval namespaceSelector: any: false matchNames: -- GitLab From bff93fb20a4f9cd5e397e47e7e1878dad2f679ec Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 17 Dec 2022 23:26:09 +0000 Subject: [PATCH 089/353] Common Method Wrappers: - updated histogram definition in Grafana dashboards - updated prometheus queries --- .../grafana_prometheus_component_rpc.json | 8 ++++---- .../grafana_prometheus_device_driver.json | 8 ++++---- .../grafana_prometheus_service_handler.json | 8 ++++---- .../tests/prometheus_queries.txt | 19 +++++++++++++++---- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json b/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json index ba088252a..b5b857e75 100644 --- a/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json +++ b/src/common/method_wrappers/tests/grafana_prometheus_component_rpc.json @@ -15,8 +15,8 @@ "editable": true, "gnetId": null, "graphTooltip": 0, - "id": 27, - "iteration": 1669995219512, + "id": 25, + "iteration": 1671297223428, "links": [], "panels": [ { @@ -174,10 +174,10 @@ "targets": [ { "exemplar": true, - "expr": "sum(increase(tfs_[[component]]_rpc_[[method]]_histogram_duration_bucket{pod=~\"[[pod]]\"}[$__rate_interval])) by (le)", + "expr": "sum(\r\n max_over_time(tfs_[[component]]_rpc_[[method]]_histogram_duration_bucket{pod=~\"[[pod]]\"}[1m]) -\r\n min_over_time(tfs_[[component]]_rpc_[[method]]_histogram_duration_bucket{pod=~\"[[pod]]\"}[1m])\r\n) by (le)", "format": "heatmap", "instant": false, - "interval": "10s", + "interval": "1m", "intervalFactor": 1, "legendFormat": "{{le}}", "queryType": "randomWalk", diff --git a/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json b/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json index a8663239b..2926a409b 100644 --- a/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json +++ b/src/common/method_wrappers/tests/grafana_prometheus_device_driver.json @@ -15,8 +15,8 @@ "editable": true, "gnetId": null, "graphTooltip": 0, - "id": 25, - "iteration": 1669993886467, + "id": 26, + "iteration": 1671318718779, "links": [], "panels": [ { @@ -174,10 +174,10 @@ "targets": [ { "exemplar": true, - "expr": "sum(increase(tfs_device_driver_[[method]]_histogram_duration_bucket{driver=~\"[[driver]]\", pod=~\"deviceservice-[[pod]]\"}[$__rate_interval])) by (le)", + "expr": "sum(\r\n max_over_time(tfs_device_driver_[[method]]_histogram_duration_bucket{driver=~\"[[driver]]\", pod=~\"deviceservice-[[pod]]\"}[1m]) -\r\n min_over_time(tfs_device_driver_[[method]]_histogram_duration_bucket{driver=~\"[[driver]]\", pod=~\"deviceservice-[[pod]]\"}[1m])\r\n) by (le)", "format": "heatmap", "instant": false, - "interval": "10s", + "interval": "60s", "intervalFactor": 1, "legendFormat": "{{le}}", "queryType": "randomWalk", diff --git a/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json b/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json index 6f65a78b2..48e770afe 100644 --- a/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json +++ b/src/common/method_wrappers/tests/grafana_prometheus_service_handler.json @@ -15,8 +15,8 @@ "editable": true, "gnetId": null, "graphTooltip": 0, - "id": 26, - "iteration": 1669992491077, + "id": 27, + "iteration": 1671319012315, "links": [], "panels": [ { @@ -175,10 +175,10 @@ "targets": [ { "exemplar": true, - "expr": "sum(increase(tfs_service_handler_[[method]]_histogram_duration_bucket{handler=~\"[[handler]]\", pod=~\"serviceservice-[[pod]]\"}[$__rate_interval])) by (le)", + "expr": "sum(\r\n max_over_time(tfs_service_handler_[[method]]_histogram_duration_bucket{handler=~\"[[handler]]\", pod=~\"serviceservice-[[pod]]\"}[1m]) -\r\n min_over_time(tfs_service_handler_[[method]]_histogram_duration_bucket{handler=~\"[[handler]]\", pod=~\"serviceservice-[[pod]]\"}[1m])\r\n) by (le)", "format": "heatmap", "instant": false, - "interval": "10s", + "interval": "1m", "intervalFactor": 1, "legendFormat": "{{le}}", "queryType": "randomWalk", diff --git a/src/common/method_wrappers/tests/prometheus_queries.txt b/src/common/method_wrappers/tests/prometheus_queries.txt index be44ad105..9eb69e970 100644 --- a/src/common/method_wrappers/tests/prometheus_queries.txt +++ b/src/common/method_wrappers/tests/prometheus_queries.txt @@ -14,7 +14,11 @@ tfs_[[component]]_rpc_[[method]]_counter_requests_started_total tfs_[[component]]_rpc_[[method]]_counter_requests_completed_total tfs_[[component]]_rpc_[[method]]_counter_requests_failed_total tfs_[[component]]_rpc_[[method]]_histogram_duration_sum -sum(increase(tfs_[[component]]_rpc_[[method]]_histogram_duration_bucket[$__rate_interval])) by (le) +#sum(increase(tfs_[[component]]_rpc_[[method]]_histogram_duration_bucket[$__rate_interval])) by (le) +sum( + max_over_time(tfs_[[component]]_rpc_[[method]]_histogram_duration_bucket{pod=~"[[pod]]"}[1m]) - + min_over_time(tfs_[[component]]_rpc_[[method]]_histogram_duration_bucket{pod=~"[[pod]]"}[1m]) +) by (le) TFS/Device/Driver: @@ -33,8 +37,11 @@ tfs_device_driver_[[method]]_counter_requests_started_total{driver="[[driver]]"} tfs_device_driver_[[method]]_counter_requests_completed_total{driver="[[driver]]"} tfs_device_driver_[[method]]_counter_requests_failed_total{driver="[[driver]]"} tfs_device_driver_[[method]]_histogram_duration_sum{driver="[[driver]]"} -sum(increase(tfs_device_driver_[[method]]_histogram_duration_bucket{driver="[[driver]]"}[$__rate_interval])) by (le) - +#sum(increase(tfs_device_driver_[[method]]_histogram_duration_bucket{driver="[[driver]]"}[$__rate_interval])) by (le) +sum( + max_over_time(tfs_device_driver_[[method]]_histogram_duration_bucket{driver="[[driver]]", pod=~"deviceservice-[[pod]]"}[1m]) - + min_over_time(tfs_device_driver_[[method]]_histogram_duration_bucket{driver="[[driver]]", pod=~"deviceservice-[[pod]]"}[1m]) +) by (le) variables: name=method @@ -49,4 +56,8 @@ tfs_service_handler_[[method]]_counter_requests_started_total{handler="[[handler tfs_service_handler_[[method]]_counter_requests_completed_total{handler="[[handler]]"} tfs_service_handler_[[method]]_counter_requests_failed_total{handler="[[handler]]"} tfs_service_handler_[[method]]_histogram_duration_sum{handler="[[handler]]"} -sum(increase(tfs_service_handler_[[method]]_histogram_duration_bucket{handler="[[handler]]"}[$__rate_interval])) by (le) +#sum(increase(tfs_service_handler_[[method]]_histogram_duration_bucket{handler="[[handler]]"}[$__rate_interval])) by (le) +sum( + max_over_time(tfs_service_handler_[[method]]_histogram_duration_bucket{handler=~"[[handler]]", pod=~"serviceservice-[[pod]]"}[1m]) - + min_over_time(tfs_service_handler_[[method]]_histogram_duration_bucket{handler=~"[[handler]]", pod=~"serviceservice-[[pod]]"}[1m]) +) by (le) -- GitLab From 4f6e6574dbbe1bc78911c48f580950dae351ca8c Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sat, 17 Dec 2022 23:27:11 +0000 Subject: [PATCH 090/353] Tool - load-gen: - updated default config - fixed issue with compatible dst endpoint selection --- src/tests/tools/load_gen/ServiceGenerator.py | 3 ++- src/tests/tools/load_gen/__main__.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tests/tools/load_gen/ServiceGenerator.py b/src/tests/tools/load_gen/ServiceGenerator.py index 632225171..86a70d9c5 100644 --- a/src/tests/tools/load_gen/ServiceGenerator.py +++ b/src/tests/tools/load_gen/ServiceGenerator.py @@ -160,13 +160,14 @@ class ServiceGenerator: # identify compatible destination endpoint types src_endpoint_type = self._endpoint_ids_to_types.get((src_device_uuid,src_endpoint_uuid)) dst_endpoint_type = ENDPOINT_COMPATIBILITY.get(src_endpoint_type) + dst_endpoint_types = {dst_endpoint_type} if service_type in {SERVICE_TYPE_TAPI} else None # identify expluded destination devices exclude_device_uuids = {} if service_type in {SERVICE_TYPE_TAPI} else {src_device_uuid} # choose feasible destination endpoint dst = self._use_device_endpoint( - service_uuid, endpoint_types={dst_endpoint_type}, exclude_device_uuids=exclude_device_uuids) + service_uuid, endpoint_types=dst_endpoint_types, exclude_device_uuids=exclude_device_uuids) # if destination endpoint not found, release source, and terminate current service generation if dst is None: diff --git a/src/tests/tools/load_gen/__main__.py b/src/tests/tools/load_gen/__main__.py index 28ffb2a9a..c1a995ebb 100644 --- a/src/tests/tools/load_gen/__main__.py +++ b/src/tests/tools/load_gen/__main__.py @@ -26,9 +26,9 @@ def main(): parameters = Parameters( num_services = 100, service_types = [ - #SERVICE_TYPE_L2NM, - #SERVICE_TYPE_L3NM, - SERVICE_TYPE_TAPI, + SERVICE_TYPE_L2NM, + SERVICE_TYPE_L3NM, + #SERVICE_TYPE_TAPI, ], offered_load = 50, holding_time = 10, -- GitLab From e7611d6df9948ccc98a628fcf6ae77ed926ffcd7 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sun, 18 Dec 2022 02:00:18 +0000 Subject: [PATCH 091/353] Service component: - added close to pathcomp compute request to enforce load balancing of requests --- src/service/service/ServiceServiceServicerImpl.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/service/service/ServiceServiceServicerImpl.py b/src/service/service/ServiceServiceServicerImpl.py index 6ce549666..bf1520270 100644 --- a/src/service/service/ServiceServiceServicerImpl.py +++ b/src/service/service/ServiceServiceServicerImpl.py @@ -116,9 +116,10 @@ class ServiceServiceServicerImpl(ServiceServiceServicer): else: pathcomp_request.k_disjoint_path.num_disjoint = num_disjoint_paths - pathcomp = PathCompClient() LOGGER.info('pathcomp_request={:s}'.format(grpc_message_to_json_string(pathcomp_request))) + pathcomp = PathCompClient() pathcomp_reply = pathcomp.Compute(pathcomp_request) + pathcomp.close() LOGGER.info('pathcomp_reply={:s}'.format(grpc_message_to_json_string(pathcomp_reply))) # Feed TaskScheduler with this path computation reply. TaskScheduler identifies inter-dependencies among -- GitLab From cd732baa360f2727b5aee9d9008e7a4f6bec0ace Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sun, 18 Dec 2022 02:00:47 +0000 Subject: [PATCH 092/353] DLT connector: - implemented RecordService method --- .../DltConnectorServiceServicerImpl.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/dlt/connector/service/DltConnectorServiceServicerImpl.py b/src/dlt/connector/service/DltConnectorServiceServicerImpl.py index 10a423166..7e07912b6 100644 --- a/src/dlt/connector/service/DltConnectorServiceServicerImpl.py +++ b/src/dlt/connector/service/DltConnectorServiceServicerImpl.py @@ -114,6 +114,34 @@ class DltConnectorServiceServicerImpl(DltConnectorServiceServicer): @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordService(self, request : DltServiceId, context : grpc.ServicerContext) -> Empty: + context_client = ContextClient() + service = context_client.GetService(request.service_id) + + dltgateway_client = DltGatewayClient() + + dlt_record_id = DltRecordId() + dlt_record_id.domain_uuid.uuid = request.topology_id.topology_uuid.uuid + dlt_record_id.type = DltRecordTypeEnum.DLTRECORDTYPE_SERVICE + dlt_record_id.record_uuid.uuid = service.service_id.service_uuid.uuid + + LOGGER.info('[RecordService] sent dlt_record_id = {:s}'.format(grpc_message_to_json_string(dlt_record_id))) + dlt_record = dltgateway_client.GetFromDlt(dlt_record_id) + LOGGER.info('[RecordService] recv dlt_record = {:s}'.format(grpc_message_to_json_string(dlt_record))) + + exists = record_exists(dlt_record) + LOGGER.info('[RecordService] exists = {:s}'.format(str(exists))) + + dlt_record = DltRecord() + dlt_record.record_id.CopyFrom(dlt_record_id) + dlt_record.operation = \ + DltRecordOperationEnum.DLTRECORDOPERATION_UPDATE \ + if exists else \ + DltRecordOperationEnum.DLTRECORDOPERATION_ADD + + dlt_record.data_json = grpc_message_to_json_string(service) + LOGGER.info('[RecordService] sent dlt_record = {:s}'.format(grpc_message_to_json_string(dlt_record))) + dlt_record_status = dltgateway_client.RecordToDlt(dlt_record) + LOGGER.info('[RecordService] recv dlt_record_status = {:s}'.format(grpc_message_to_json_string(dlt_record_status))) return Empty() @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) -- GitLab From 19a6064488b7edc817a1e4a058e1b7e7925e586d Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sun, 18 Dec 2022 02:01:39 +0000 Subject: [PATCH 093/353] Tool - load-gen: - added support to request slices --- src/tests/tools/load_gen/Constants.py | 8 +- src/tests/tools/load_gen/Parameters.py | 10 +- ...erviceGenerator.py => RequestGenerator.py} | 170 ++++++++++++++---- src/tests/tools/load_gen/RequestScheduler.py | 144 +++++++++++++++ src/tests/tools/load_gen/ServiceScheduler.py | 106 ----------- src/tests/tools/load_gen/__main__.py | 26 +-- 6 files changed, 308 insertions(+), 156 deletions(-) rename src/tests/tools/load_gen/{ServiceGenerator.py => RequestGenerator.py} (62%) create mode 100644 src/tests/tools/load_gen/RequestScheduler.py delete mode 100644 src/tests/tools/load_gen/ServiceScheduler.py diff --git a/src/tests/tools/load_gen/Constants.py b/src/tests/tools/load_gen/Constants.py index 28c1c65be..94d80bfdf 100644 --- a/src/tests/tools/load_gen/Constants.py +++ b/src/tests/tools/load_gen/Constants.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -SERVICE_TYPE_L2NM = 'l2nm' -SERVICE_TYPE_L3NM = 'l3nm' -SERVICE_TYPE_TAPI = 'tapi' +REQUEST_TYPE_SERVICE_L2NM = 'svc-l2nm' +REQUEST_TYPE_SERVICE_L3NM = 'svc-l3nm' +REQUEST_TYPE_SERVICE_TAPI = 'svc-tapi' +REQUEST_TYPE_SLICE_L2NM = 'slc-l2nm' +REQUEST_TYPE_SLICE_L3NM = 'slc-l3nm' diff --git a/src/tests/tools/load_gen/Parameters.py b/src/tests/tools/load_gen/Parameters.py index fa2ac01f3..9aab3b147 100644 --- a/src/tests/tools/load_gen/Parameters.py +++ b/src/tests/tools/load_gen/Parameters.py @@ -16,12 +16,12 @@ from typing import List, Optional class Parameters: def __init__( - self, num_services : int, service_types : List[str], offered_load : Optional[float] = None, + self, num_requests : int, request_types : List[str], offered_load : Optional[float] = None, inter_arrival_time : Optional[float] = None, holding_time : Optional[float] = None, dry_mode : bool = False ) -> None: - self._num_services = num_services - self._service_types = service_types + self._num_requests = num_requests + self._request_types = request_types self._offered_load = offered_load self._inter_arrival_time = inter_arrival_time self._holding_time = holding_time @@ -35,10 +35,10 @@ class Parameters: self._holding_time = self._offered_load * self._inter_arrival_time @property - def num_services(self): return self._num_services + def num_requests(self): return self._num_requests @property - def service_types(self): return self._service_types + def request_types(self): return self._request_types @property def offered_load(self): return self._offered_load diff --git a/src/tests/tools/load_gen/ServiceGenerator.py b/src/tests/tools/load_gen/RequestGenerator.py similarity index 62% rename from src/tests/tools/load_gen/ServiceGenerator.py rename to src/tests/tools/load_gen/RequestGenerator.py index 86a70d9c5..6d0be9d9c 100644 --- a/src/tests/tools/load_gen/ServiceGenerator.py +++ b/src/tests/tools/load_gen/RequestGenerator.py @@ -21,8 +21,11 @@ from common.tools.object_factory.Device import json_device_id from common.tools.object_factory.EndPoint import json_endpoint_id from common.tools.object_factory.Service import ( json_service_l2nm_planned, json_service_l3nm_planned, json_service_tapi_planned) +from common.tools.object_factory.Slice import json_slice from context.client.ContextClient import ContextClient -from .Constants import SERVICE_TYPE_L2NM, SERVICE_TYPE_L3NM, SERVICE_TYPE_TAPI +from .Constants import ( + REQUEST_TYPE_SERVICE_L2NM, REQUEST_TYPE_SERVICE_L3NM, REQUEST_TYPE_SERVICE_TAPI, + REQUEST_TYPE_SLICE_L2NM, REQUEST_TYPE_SLICE_L3NM) from .Parameters import Parameters LOGGER = logging.getLogger(__name__) @@ -32,11 +35,11 @@ ENDPOINT_COMPATIBILITY = { 'PHOTONIC_MEDIA:DWDM:G_50GHZ:INPUT' : 'PHOTONIC_MEDIA:DWDM:G_50GHZ:OUTPUT', } -class ServiceGenerator: +class RequestGenerator: def __init__(self, parameters : Parameters) -> None: self._parameters = parameters self._lock = threading.Lock() - self._num_services = 0 + self._num_requests = 0 self._available_device_endpoints : Dict[str, Set[str]] = dict() self._used_device_endpoints : Dict[str, Dict[str, str]] = dict() self._endpoint_ids_to_types : Dict[Tuple[str, str], str] = dict() @@ -79,7 +82,7 @@ class ServiceGenerator: endpoints_for_type.discard(endpoint_key) @property - def num_services_generated(self): return self._num_services + def num_requests_generated(self): return self._num_requests def dump_state(self) -> None: with self._lock: @@ -139,19 +142,26 @@ class ServiceGenerator: self._used_device_endpoints.setdefault(device_uuid, set()).pop(endpoint_uuid, None) self._available_device_endpoints.setdefault(device_uuid, set()).add(endpoint_uuid) - def compose_service(self) -> Optional[Dict]: + def compose_request(self) -> Optional[Dict]: with self._lock: - self._num_services += 1 - num_service = self._num_services - #service_uuid = str(uuid.uuid4()) - service_uuid = 'svc_{:d}'.format(num_service) + self._num_requests += 1 + num_request = self._num_requests + + #request_uuid = str(uuid.uuid4()) + request_uuid = 'svc_{:d}'.format(num_request) - # choose service type - service_type = random.choice(self._parameters.service_types) + # choose request type + request_type = random.choice(self._parameters.request_types) + + if request_type in {REQUEST_TYPE_SERVICE_L2NM, REQUEST_TYPE_SERVICE_L3NM, REQUEST_TYPE_SERVICE_TAPI}: + return self._compose_service(num_request, request_uuid, request_type) + elif request_type in {REQUEST_TYPE_SLICE_L2NM, REQUEST_TYPE_SLICE_L3NM}: + return self._compose_slice(num_request, request_uuid, request_type) + def _compose_service(self, num_request : int, request_uuid : str, request_type : str) -> Optional[Dict]: # choose source endpoint - src_endpoint_types = set(ENDPOINT_COMPATIBILITY.keys()) if service_type in {SERVICE_TYPE_TAPI} else None - src = self._use_device_endpoint(service_uuid, endpoint_types=src_endpoint_types) + src_endpoint_types = set(ENDPOINT_COMPATIBILITY.keys()) if request_type in {REQUEST_TYPE_SERVICE_TAPI} else None + src = self._use_device_endpoint(request_uuid, endpoint_types=src_endpoint_types) if src is None: LOGGER.warning('>> No source endpoint is available') return None @@ -160,14 +170,14 @@ class ServiceGenerator: # identify compatible destination endpoint types src_endpoint_type = self._endpoint_ids_to_types.get((src_device_uuid,src_endpoint_uuid)) dst_endpoint_type = ENDPOINT_COMPATIBILITY.get(src_endpoint_type) - dst_endpoint_types = {dst_endpoint_type} if service_type in {SERVICE_TYPE_TAPI} else None + dst_endpoint_types = {dst_endpoint_type} if request_type in {REQUEST_TYPE_SERVICE_TAPI} else None - # identify expluded destination devices - exclude_device_uuids = {} if service_type in {SERVICE_TYPE_TAPI} else {src_device_uuid} + # identify excluded destination devices + exclude_device_uuids = {} if request_type in {REQUEST_TYPE_SERVICE_TAPI} else {src_device_uuid} # choose feasible destination endpoint dst = self._use_device_endpoint( - service_uuid, endpoint_types=dst_endpoint_types, exclude_device_uuids=exclude_device_uuids) + request_uuid, endpoint_types=dst_endpoint_types, exclude_device_uuids=exclude_device_uuids) # if destination endpoint not found, release source, and terminate current service generation if dst is None: @@ -182,12 +192,12 @@ class ServiceGenerator: json_endpoint_id(json_device_id(dst_device_uuid), dst_endpoint_uuid), ] - if service_type == SERVICE_TYPE_L2NM: + if request_type == REQUEST_TYPE_SERVICE_L2NM: constraints = [ json_constraint_custom('bandwidth[gbps]', '10.0'), json_constraint_custom('latency[ms]', '20.0'), ] - vlan_id = num_service % 1000 + vlan_id = num_request % 1000 circuit_id = '{:03d}'.format(vlan_id) src_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', ''))) dst_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', ''))) @@ -211,15 +221,15 @@ class ServiceGenerator: }), ] return json_service_l2nm_planned( - service_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules) + request_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules) - elif service_type == SERVICE_TYPE_L3NM: + elif request_type == REQUEST_TYPE_SERVICE_L3NM: constraints = [ json_constraint_custom('bandwidth[gbps]', '10.0'), json_constraint_custom('latency[ms]', '20.0'), ] - vlan_id = num_service % 1000 - bgp_as = 60000 + (num_service % 10000) + vlan_id = num_request % 1000 + bgp_as = 60000 + (num_request % 10000) bgp_route_target = '{:5d}:{:03d}'.format(bgp_as, 333) route_distinguisher = '{:5d}:{:03d}'.format(bgp_as, vlan_id) src_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', ''))) @@ -250,9 +260,9 @@ class ServiceGenerator: }), ] return json_service_l3nm_planned( - service_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules) + request_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules) - elif service_type == SERVICE_TYPE_TAPI: + elif request_type == REQUEST_TYPE_SERVICE_TAPI: config_rules = [ json_config_rule_set('/settings', { 'capacity_value' : 50.0, @@ -263,10 +273,108 @@ class ServiceGenerator: }), ] return json_service_tapi_planned( - service_uuid, endpoint_ids=endpoint_ids, constraints=[], config_rules=config_rules) + request_uuid, endpoint_ids=endpoint_ids, constraints=[], config_rules=config_rules) + + def _compose_slice(self, num_request : int, request_uuid : str, request_type : str) -> Optional[Dict]: + # choose source endpoint + src = self._use_device_endpoint(request_uuid) + if src is None: + LOGGER.warning('>> No source endpoint is available') + return None + src_device_uuid,src_endpoint_uuid = src + + # identify excluded destination devices + exclude_device_uuids = {} if request_type in {REQUEST_TYPE_SERVICE_TAPI} else {src_device_uuid} + + # choose feasible destination endpoint + dst = self._use_device_endpoint(request_uuid, exclude_device_uuids=exclude_device_uuids) + + # if destination endpoint not found, release source, and terminate current service generation + if dst is None: + LOGGER.warning('>> No destination endpoint is available') + self._release_device_endpoint(src_device_uuid, src_endpoint_uuid) + return None + + # compose endpoints + dst_device_uuid,dst_endpoint_uuid = dst + endpoint_ids = [ + json_endpoint_id(json_device_id(src_device_uuid), src_endpoint_uuid), + json_endpoint_id(json_device_id(dst_device_uuid), dst_endpoint_uuid), + ] + constraints = [ + json_constraint_custom('bandwidth[gbps]', '10.0'), + json_constraint_custom('latency[ms]', '20.0'), + ] + + if request_type == REQUEST_TYPE_SLICE_L2NM: + vlan_id = num_request % 1000 + circuit_id = '{:03d}'.format(vlan_id) + src_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', ''))) + dst_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', ''))) + config_rules = [ + json_config_rule_set('/settings', { + 'mtu': 1512 + }), + json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(src_device_uuid, src_endpoint_uuid), { + 'router_id': src_router_id, + 'sub_interface_index': vlan_id, + 'vlan_id': vlan_id, + 'remote_router': dst_router_id, + 'circuit_id': circuit_id, + }), + json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(dst_device_uuid, dst_endpoint_uuid), { + 'router_id': dst_router_id, + 'sub_interface_index': vlan_id, + 'vlan_id': vlan_id, + 'remote_router': src_router_id, + 'circuit_id': circuit_id, + }), + ] + + elif request_type == REQUEST_TYPE_SLICE_L3NM: + vlan_id = num_request % 1000 + bgp_as = 60000 + (num_request % 10000) + bgp_route_target = '{:5d}:{:03d}'.format(bgp_as, 333) + route_distinguisher = '{:5d}:{:03d}'.format(bgp_as, vlan_id) + src_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', ''))) + dst_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', ''))) + src_address_ip = '.'.join([src_device_uuid.replace('R', ''), '0'] + src_endpoint_uuid.split('/')) + dst_address_ip = '.'.join([dst_device_uuid.replace('R', ''), '0'] + dst_endpoint_uuid.split('/')) + config_rules = [ + json_config_rule_set('/settings', { + 'mtu' : 1512, + 'bgp_as' : bgp_as, + 'bgp_route_target': bgp_route_target, + }), + json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(src_device_uuid, src_endpoint_uuid), { + 'router_id' : src_router_id, + 'route_distinguisher': route_distinguisher, + 'sub_interface_index': vlan_id, + 'vlan_id' : vlan_id, + 'address_ip' : src_address_ip, + 'address_prefix' : 16, + }), + json_config_rule_set('/device[{:s}]/endpoint[{:s}]/settings'.format(dst_device_uuid, dst_endpoint_uuid), { + 'router_id' : dst_router_id, + 'route_distinguisher': route_distinguisher, + 'sub_interface_index': vlan_id, + 'vlan_id' : vlan_id, + 'address_ip' : dst_address_ip, + 'address_prefix' : 16, + }), + ] + + return json_slice( + request_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules) - def release_service(self, json_service : Dict) -> None: - for endpoint_id in json_service['service_endpoint_ids']: - device_uuid = endpoint_id['device_id']['device_uuid']['uuid'] - endpoint_uuid = endpoint_id['endpoint_uuid']['uuid'] - self._release_device_endpoint(device_uuid, endpoint_uuid) + def release_request(self, json_request : Dict) -> None: + if 'service_id' in json_request: + for endpoint_id in json_request['service_endpoint_ids']: + device_uuid = endpoint_id['device_id']['device_uuid']['uuid'] + endpoint_uuid = endpoint_id['endpoint_uuid']['uuid'] + self._release_device_endpoint(device_uuid, endpoint_uuid) + elif 'slice_id' in json_request: + for endpoint_id in json_request['slice_endpoint_ids']: + device_uuid = endpoint_id['device_id']['device_uuid']['uuid'] + endpoint_uuid = endpoint_id['endpoint_uuid']['uuid'] + self._release_device_endpoint(device_uuid, endpoint_uuid) diff --git a/src/tests/tools/load_gen/RequestScheduler.py b/src/tests/tools/load_gen/RequestScheduler.py new file mode 100644 index 000000000..5be579c71 --- /dev/null +++ b/src/tests/tools/load_gen/RequestScheduler.py @@ -0,0 +1,144 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 copy, logging, pytz, random +from datetime import datetime, timedelta +from apscheduler.executors.pool import ThreadPoolExecutor +from apscheduler.jobstores.memory import MemoryJobStore +from apscheduler.schedulers.blocking import BlockingScheduler +from typing import Dict +from common.proto.context_pb2 import Service, ServiceId, Slice, SliceId +from service.client.ServiceClient import ServiceClient +from slice.client.SliceClient import SliceClient +from .Parameters import Parameters +from .RequestGenerator import RequestGenerator + +logging.getLogger('apscheduler.executors.default').setLevel(logging.WARNING) +logging.getLogger('apscheduler.scheduler').setLevel(logging.WARNING) + +LOGGER = logging.getLogger(__name__) + +class RequestScheduler: + def __init__(self, parameters : Parameters, generator : RequestGenerator) -> None: + self._scheduler = BlockingScheduler() + self._scheduler.configure( + jobstores = {'default': MemoryJobStore()}, + executors = {'default': ThreadPoolExecutor(max_workers=10)}, + job_defaults = { + 'coalesce': False, + 'max_instances': 100, + 'misfire_grace_time': 120, + }, + timezone=pytz.utc) + self._parameters = parameters + self._generator = generator + + def _schedule_request_setup(self) -> None: + if self._generator.num_requests_generated >= self._parameters.num_requests: + LOGGER.info('Generation Done!') + #self._scheduler.shutdown() + return + iat = random.expovariate(1.0 / self._parameters.inter_arrival_time) + run_date = datetime.utcnow() + timedelta(seconds=iat) + self._scheduler.add_job( + self._request_setup, trigger='date', run_date=run_date, timezone=pytz.utc) + + def _schedule_request_teardown(self, request : Dict) -> None: + ht = random.expovariate(1.0 / self._parameters.holding_time) + run_date = datetime.utcnow() + timedelta(seconds=ht) + self._scheduler.add_job( + self._request_teardown, args=(request,), trigger='date', run_date=run_date, timezone=pytz.utc) + + def start(self): + self._schedule_request_setup() + self._scheduler.start() + + def _request_setup(self) -> None: + self._schedule_request_setup() + + request = self._generator.compose_request() + if request is None: + LOGGER.warning('No resources available to compose new request') + return + + if 'service_id' in request: + service_uuid = request['service_id']['service_uuid']['uuid'] + src_device_uuid = request['service_endpoint_ids'][0]['device_id']['device_uuid']['uuid'] + src_endpoint_uuid = request['service_endpoint_ids'][0]['endpoint_uuid']['uuid'] + dst_device_uuid = request['service_endpoint_ids'][1]['device_id']['device_uuid']['uuid'] + dst_endpoint_uuid = request['service_endpoint_ids'][1]['endpoint_uuid']['uuid'] + LOGGER.info('Setup Service: uuid=%s src=%s:%s dst=%s:%s', + service_uuid, src_device_uuid, src_endpoint_uuid, dst_device_uuid, dst_endpoint_uuid) + + if not self._parameters.dry_mode: + request_add = copy.deepcopy(request) + request_add['service_endpoint_ids'] = [] + request_add['service_constraints'] = [] + request_add['service_config'] = {'config_rules': []} + service_client = ServiceClient() # create instances per request to load balance between pods + service_client.CreateService(Service(**request_add)) + service_client.UpdateService(Service(**request)) + service_client.close() + + elif 'slice_id' in request: + slice_uuid = request['slice_id']['slice_uuid']['uuid'] + src_device_uuid = request['slice_endpoint_ids'][0]['device_id']['device_uuid']['uuid'] + src_endpoint_uuid = request['slice_endpoint_ids'][0]['endpoint_uuid']['uuid'] + dst_device_uuid = request['slice_endpoint_ids'][1]['device_id']['device_uuid']['uuid'] + dst_endpoint_uuid = request['slice_endpoint_ids'][1]['endpoint_uuid']['uuid'] + LOGGER.info('Setup Slice: uuid=%s src=%s:%s dst=%s:%s', + slice_uuid, src_device_uuid, src_endpoint_uuid, dst_device_uuid, dst_endpoint_uuid) + + if not self._parameters.dry_mode: + request_add = copy.deepcopy(request) + request_add['slice_endpoint_ids'] = [] + request_add['slice_constraints'] = [] + request_add['slice_config'] = {'config_rules': []} + slice_client = SliceClient() # create instances per request to load balance between pods + slice_client.CreateSlice(Slice(**request_add)) + slice_client.UpdateSlice(Slice(**request)) + slice_client.close() + + self._schedule_request_teardown(request) + + def _request_teardown(self, request : Dict) -> None: + if 'service_id' in request: + service_uuid = request['service_id']['service_uuid']['uuid'] + src_device_uuid = request['service_endpoint_ids'][0]['device_id']['device_uuid']['uuid'] + src_endpoint_uuid = request['service_endpoint_ids'][0]['endpoint_uuid']['uuid'] + dst_device_uuid = request['service_endpoint_ids'][1]['device_id']['device_uuid']['uuid'] + dst_endpoint_uuid = request['service_endpoint_ids'][1]['endpoint_uuid']['uuid'] + LOGGER.info('Teardown Service: uuid=%s src=%s:%s dst=%s:%s', + service_uuid, src_device_uuid, src_endpoint_uuid, dst_device_uuid, dst_endpoint_uuid) + + if not self._parameters.dry_mode: + service_client = ServiceClient() # create instances per request to load balance between pods + service_client.DeleteService(ServiceId(**(request['service_id']))) + service_client.close() + + elif 'slice_id' in request: + slice_uuid = request['slice_id']['slice_uuid']['uuid'] + src_device_uuid = request['slice_endpoint_ids'][0]['device_id']['device_uuid']['uuid'] + src_endpoint_uuid = request['slice_endpoint_ids'][0]['endpoint_uuid']['uuid'] + dst_device_uuid = request['slice_endpoint_ids'][1]['device_id']['device_uuid']['uuid'] + dst_endpoint_uuid = request['slice_endpoint_ids'][1]['endpoint_uuid']['uuid'] + LOGGER.info('Teardown Slice: uuid=%s src=%s:%s dst=%s:%s', + slice_uuid, src_device_uuid, src_endpoint_uuid, dst_device_uuid, dst_endpoint_uuid) + + if not self._parameters.dry_mode: + slice_client = SliceClient() # create instances per request to load balance between pods + slice_client.DeleteSlice(SliceId(**(request['slice_id']))) + slice_client.close() + + self._generator.release_request(request) diff --git a/src/tests/tools/load_gen/ServiceScheduler.py b/src/tests/tools/load_gen/ServiceScheduler.py deleted file mode 100644 index 5a8b8dbdf..000000000 --- a/src/tests/tools/load_gen/ServiceScheduler.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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 copy, logging, pytz, random -from datetime import datetime, timedelta -from apscheduler.executors.pool import ThreadPoolExecutor -from apscheduler.jobstores.memory import MemoryJobStore -from apscheduler.schedulers.blocking import BlockingScheduler -from typing import Dict -from common.proto.context_pb2 import Service, ServiceId -from service.client.ServiceClient import ServiceClient -from .Parameters import Parameters -from .ServiceGenerator import ServiceGenerator - -logging.getLogger('apscheduler.executors.default').setLevel(logging.WARNING) -logging.getLogger('apscheduler.scheduler').setLevel(logging.WARNING) - -LOGGER = logging.getLogger(__name__) - -class ServiceScheduler: - def __init__(self, parameters : Parameters, service_generator : ServiceGenerator) -> None: - self._scheduler = BlockingScheduler() - self._scheduler.configure( - jobstores = {'default': MemoryJobStore()}, - executors = {'default': ThreadPoolExecutor(max_workers=10)}, - job_defaults = { - 'coalesce': False, - 'max_instances': 100, - 'misfire_grace_time': 120, - }, - timezone=pytz.utc) - self._parameters = parameters - self._service_generator = service_generator - - def _schedule_service_setup(self) -> None: - if self._service_generator.num_services_generated >= self._parameters.num_services: - LOGGER.info('Generation Done!') - #self._scheduler.shutdown() - return - iat = random.expovariate(1.0 / self._parameters.inter_arrival_time) - run_date = datetime.utcnow() + timedelta(seconds=iat) - self._scheduler.add_job( - self._service_setup, trigger='date', run_date=run_date, timezone=pytz.utc) - - def _schedule_service_teardown(self, service : Dict) -> None: - ht = random.expovariate(1.0 / self._parameters.holding_time) - run_date = datetime.utcnow() + timedelta(seconds=ht) - self._scheduler.add_job( - self._service_teardown, args=(service,), trigger='date', run_date=run_date, timezone=pytz.utc) - - def start(self): - self._schedule_service_setup() - self._scheduler.start() - - def _service_setup(self) -> None: - self._schedule_service_setup() - - service = self._service_generator.compose_service() - if service is None: - LOGGER.warning('No resources available to compose new service') - return - - service_uuid = service['service_id']['service_uuid']['uuid'] - src_device_uuid = service['service_endpoint_ids'][0]['device_id']['device_uuid']['uuid'] - src_endpoint_uuid = service['service_endpoint_ids'][0]['endpoint_uuid']['uuid'] - dst_device_uuid = service['service_endpoint_ids'][1]['device_id']['device_uuid']['uuid'] - dst_endpoint_uuid = service['service_endpoint_ids'][1]['endpoint_uuid']['uuid'] - LOGGER.info('Setup Service: uuid=%s src=%s:%s dst=%s:%s', - service_uuid, src_device_uuid, src_endpoint_uuid, dst_device_uuid, dst_endpoint_uuid) - - if not self._parameters.dry_mode: - service_add = copy.deepcopy(service) - service_add['service_endpoint_ids'] = [] - service_add['service_constraints'] = [] - service_add['service_config'] = {'config_rules': []} - service_client = ServiceClient() # create instances per request to load balance between pods - service_client.CreateService(Service(**service_add)) - service_client.UpdateService(Service(**service)) - - self._schedule_service_teardown(service) - - def _service_teardown(self, service : Dict) -> None: - service_uuid = service['service_id']['service_uuid']['uuid'] - src_device_uuid = service['service_endpoint_ids'][0]['device_id']['device_uuid']['uuid'] - src_endpoint_uuid = service['service_endpoint_ids'][0]['endpoint_uuid']['uuid'] - dst_device_uuid = service['service_endpoint_ids'][1]['device_id']['device_uuid']['uuid'] - dst_endpoint_uuid = service['service_endpoint_ids'][1]['endpoint_uuid']['uuid'] - LOGGER.info('Teardown Service: uuid=%s src=%s:%s dst=%s:%s', - service_uuid, src_device_uuid, src_endpoint_uuid, dst_device_uuid, dst_endpoint_uuid) - - if not self._parameters.dry_mode: - service_client = ServiceClient() # create instances per request to load balance between pods - service_client.DeleteService(ServiceId(**(service['service_id']))) - - self._service_generator.release_service(service) diff --git a/src/tests/tools/load_gen/__main__.py b/src/tests/tools/load_gen/__main__.py index c1a995ebb..f5d9d364d 100644 --- a/src/tests/tools/load_gen/__main__.py +++ b/src/tests/tools/load_gen/__main__.py @@ -13,10 +13,12 @@ # limitations under the License. import logging, sys -from .Constants import SERVICE_TYPE_L2NM, SERVICE_TYPE_L3NM, SERVICE_TYPE_TAPI +from .Constants import ( + REQUEST_TYPE_SERVICE_L2NM, REQUEST_TYPE_SERVICE_L3NM, REQUEST_TYPE_SERVICE_TAPI, + REQUEST_TYPE_SLICE_L2NM, REQUEST_TYPE_SLICE_L3NM) from .Parameters import Parameters -from .ServiceGenerator import ServiceGenerator -from .ServiceScheduler import ServiceScheduler +from .RequestGenerator import RequestGenerator +from .RequestScheduler import RequestScheduler logging.basicConfig(level=logging.INFO) LOGGER = logging.getLogger(__name__) @@ -24,11 +26,13 @@ LOGGER = logging.getLogger(__name__) def main(): LOGGER.info('Starting...') parameters = Parameters( - num_services = 100, - service_types = [ - SERVICE_TYPE_L2NM, - SERVICE_TYPE_L3NM, - #SERVICE_TYPE_TAPI, + num_requests = 100, + request_types = [ + REQUEST_TYPE_SERVICE_L2NM, + REQUEST_TYPE_SERVICE_L3NM, + REQUEST_TYPE_SERVICE_TAPI, + REQUEST_TYPE_SLICE_L2NM, + REQUEST_TYPE_SLICE_L3NM, ], offered_load = 50, holding_time = 10, @@ -36,11 +40,11 @@ def main(): ) LOGGER.info('Initializing Generator...') - service_generator = ServiceGenerator(parameters) - service_generator.initialize() + generator = RequestGenerator(parameters) + generator.initialize() LOGGER.info('Running Schedule...') - scheduler = ServiceScheduler(parameters, service_generator) + scheduler = RequestScheduler(parameters, generator) scheduler.start() LOGGER.info('Done!') -- GitLab From fe5b4e32b75c8c1b300ffe1c1efa1d171324682e Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sun, 18 Dec 2022 02:02:24 +0000 Subject: [PATCH 094/353] Tool - dlt-load-gen: - new tool (under implementation) to trigger recording of entities in DLT and evaluate performance --- src/tests/tools/dlt_load_gen/__init__.py | 14 ++ src/tests/tools/dlt_load_gen/__main__.py | 82 +++++++ src/tests/tools/dlt_load_gen/deploy_specs.sh | 26 ++ src/tests/tools/dlt_load_gen/descriptors.json | 229 ++++++++++++++++++ src/tests/tools/dlt_load_gen/run.sh | 17 ++ 5 files changed, 368 insertions(+) create mode 100644 src/tests/tools/dlt_load_gen/__init__.py create mode 100644 src/tests/tools/dlt_load_gen/__main__.py create mode 100644 src/tests/tools/dlt_load_gen/deploy_specs.sh create mode 100644 src/tests/tools/dlt_load_gen/descriptors.json create mode 100755 src/tests/tools/dlt_load_gen/run.sh diff --git a/src/tests/tools/dlt_load_gen/__init__.py b/src/tests/tools/dlt_load_gen/__init__.py new file mode 100644 index 000000000..70a332512 --- /dev/null +++ b/src/tests/tools/dlt_load_gen/__init__.py @@ -0,0 +1,14 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + diff --git a/src/tests/tools/dlt_load_gen/__main__.py b/src/tests/tools/dlt_load_gen/__main__.py new file mode 100644 index 000000000..cdad0662c --- /dev/null +++ b/src/tests/tools/dlt_load_gen/__main__.py @@ -0,0 +1,82 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 logging, sys +from common.proto.context_pb2 import DeviceId, Empty, LinkId, ServiceId, SliceId, TopologyId +from common.proto.dlt_connector_pb2 import DltDeviceId, DltLinkId, DltServiceId, DltSliceId +from common.tools.object_factory.Topology import json_topology_id +from context.client.ContextClient import ContextClient +from dlt.connector.client.DltConnectorClient import DltConnectorClient + +logging.basicConfig(level=logging.INFO) +LOGGER = logging.getLogger(__name__) + +def record_device(dlt_connector_client : DltConnectorClient, domain_id : TopologyId, device_id : DeviceId): + dlt_device_id = DltDeviceId() + dlt_device_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member + dlt_device_id.device_id.CopyFrom(device_id) # pylint: disable=no-member + dlt_connector_client.RecordDevice(dlt_device_id) + +def record_link(dlt_connector_client : DltConnectorClient, domain_id : TopologyId, link_id : LinkId): + dlt_link_id = DltLinkId() + dlt_link_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member + dlt_link_id.link_id.CopyFrom(link_id) # pylint: disable=no-member + dlt_connector_client.RecordLink(dlt_link_id) + +def record_service(dlt_connector_client : DltConnectorClient, domain_id : TopologyId, service_id : ServiceId): + dlt_service_id = DltServiceId() + dlt_service_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member + dlt_service_id.service_id.CopyFrom(service_id) # pylint: disable=no-member + dlt_connector_client.RecordService(dlt_service_id) + +def record_slice(dlt_connector_client : DltConnectorClient, domain_id : TopologyId, slice_id : SliceId): + dlt_slice_id = DltSliceId() + dlt_slice_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member + dlt_slice_id.slice_id.CopyFrom(slice_id) # pylint: disable=no-member + dlt_connector_client.RecordSlice(dlt_slice_id) + +def main(): + LOGGER.info('Starting...') + + context_client = ContextClient() + dlt_connector_client = DltConnectorClient() + + domain_id = TopologyId(**json_topology_id('dlt-perf-eval')) + + device_ids = context_client.ListDeviceIds(Empty()) + for device_id in device_ids.device_ids: + record_device(dlt_connector_client, domain_id, device_id) + + link_ids = context_client.ListLinkIds(Empty()) + for link_id in link_ids.link_ids: + record_link(dlt_connector_client, domain_id, link_id) + + context_ids = context_client.ListContextIds(Empty()) + for context_id in context_ids.context_ids: + service_ids = context_client.ListServiceIds(context_id) + for service_id in service_ids.service_ids: + record_service(dlt_connector_client, domain_id, service_id) + + slice_ids = context_client.ListSliceIds(context_id) + for slice_id in slice_ids.slice_ids: + record_slice(dlt_connector_client, domain_id, slice_id) + + LOGGER.info('Done!') + return 0 + +if __name__ == '__main__': + sys.exit(main()) + + + diff --git a/src/tests/tools/dlt_load_gen/deploy_specs.sh b/src/tests/tools/dlt_load_gen/deploy_specs.sh new file mode 100644 index 000000000..1982ef227 --- /dev/null +++ b/src/tests/tools/dlt_load_gen/deploy_specs.sh @@ -0,0 +1,26 @@ +# Set the URL of your local Docker registry where the images will be uploaded to. +export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" + +# Set the list of components, separated by spaces, you want to build images for, and deploy. +# Supported components are: +# context device automation policy service compute monitoring webui +# interdomain slice pathcomp dlt +# dbscanserving opticalattackmitigator opticalattackdetector +# l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector +export TFS_COMPONENTS="context device pathcomp service slice webui dlt" # automation monitoring compute + +# Set the tag you want to use for your images. +export TFS_IMAGE_TAG="dev" + +# Set the name of the Kubernetes namespace to deploy to. +export TFS_K8S_NAMESPACE="tfs" + +# Set additional manifest files to be applied after the deployment +export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml manifests/servicemonitors.yaml" + +# Set the new Grafana admin password +export TFS_GRAFANA_PASSWORD="admin123+" + +# If not already set, disable skip-build flag. +# If TFS_SKIP_BUILD is "YES", the containers are not rebuilt-retagged-repushed and existing ones are used. +export TFS_SKIP_BUILD="NO" #${TFS_SKIP_BUILD:-"YES"} diff --git a/src/tests/tools/dlt_load_gen/descriptors.json b/src/tests/tools/dlt_load_gen/descriptors.json new file mode 100644 index 000000000..5fb0c0867 --- /dev/null +++ b/src/tests/tools/dlt_load_gen/descriptors.json @@ -0,0 +1,229 @@ +{ + "contexts": [ + { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_ids": [], "service_ids": [] + } + ], + "topologies": [ + { + "topology_id": { + "context_id": {"context_uuid": {"uuid": "admin"}}, + "topology_uuid": {"uuid": "admin"} + }, + "device_ids": [ + {"device_uuid": {"uuid": "R1"}}, + {"device_uuid": {"uuid": "R2"}}, + {"device_uuid": {"uuid": "R3"}}, + {"device_uuid": {"uuid": "R4"}}, + {"device_uuid": {"uuid": "R5"}}, + {"device_uuid": {"uuid": "R6"}}, + {"device_uuid": {"uuid": "R7"}} + ], + "link_ids": [ + {"link_uuid": {"uuid": "R1==R2"}}, + {"link_uuid": {"uuid": "R2==R3"}}, + {"link_uuid": {"uuid": "R3==R4"}}, + {"link_uuid": {"uuid": "R4==R5"}}, + {"link_uuid": {"uuid": "R5==R6"}}, + {"link_uuid": {"uuid": "R6==R1"}}, + {"link_uuid": {"uuid": "R1==R7"}}, + {"link_uuid": {"uuid": "R3==R7"}}, + {"link_uuid": {"uuid": "R5==R7"}} + ] + } + ], + "devices": [ + { + "device_id": {"device_uuid": {"uuid": "R1"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R2"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R3"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R4"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R5"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R6"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "1/4"}, + {"sample_types": [], "type": "copper", "uuid": "1/5"}, + {"sample_types": [], "type": "copper", "uuid": "1/6"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"} + ]}}} + ]} + }, + { + "device_id": {"device_uuid": {"uuid": "R7"}}, "device_type": "emu-packet-router", "device_drivers": [0], + "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ + {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, + {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ + {"sample_types": [], "type": "copper", "uuid": "1/1"}, + {"sample_types": [], "type": "copper", "uuid": "1/2"}, + {"sample_types": [], "type": "copper", "uuid": "1/3"}, + {"sample_types": [], "type": "copper", "uuid": "2/1"}, + {"sample_types": [], "type": "copper", "uuid": "2/2"}, + {"sample_types": [], "type": "copper", "uuid": "2/3"}, + {"sample_types": [], "type": "copper", "uuid": "2/4"}, + {"sample_types": [], "type": "copper", "uuid": "2/5"}, + {"sample_types": [], "type": "copper", "uuid": "2/6"} + ]}}} + ]} + } + ], + "links": [ + { + "link_id": {"link_uuid": {"uuid": "R1==R2"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R2==R3"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R3==R4"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R4==R5"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R5==R6"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R6"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R6==R1"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R6"}}, "endpoint_uuid": {"uuid": "2/1"}}, + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/2"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R1==R7"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/3"}}, + {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/1"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R3==R7"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/3"}}, + {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/3"}} + ] + }, + { + "link_id": {"link_uuid": {"uuid": "R5==R7"}}, + "link_endpoint_ids": [ + {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/3"}}, + {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/5"}} + ] + } + ] +} \ No newline at end of file diff --git a/src/tests/tools/dlt_load_gen/run.sh b/src/tests/tools/dlt_load_gen/run.sh new file mode 100755 index 000000000..6329b5434 --- /dev/null +++ b/src/tests/tools/dlt_load_gen/run.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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. + +source tfs_runtime_env_vars.sh +python -m tests.tools.dlt_load_gen -- GitLab From 56badde1ff5c5028f14684a400e0569ab044393d Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sun, 18 Dec 2022 20:29:00 +0000 Subject: [PATCH 095/353] Common: - improved object-factry slice-related methods --- src/common/tools/object_factory/Slice.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/tools/object_factory/Slice.py b/src/common/tools/object_factory/Slice.py index 6ab666aa6..2376784e3 100644 --- a/src/common/tools/object_factory/Slice.py +++ b/src/common/tools/object_factory/Slice.py @@ -14,7 +14,9 @@ import copy from typing import Dict, List, Optional +from common.Constants import DEFAULT_CONTEXT_UUID from common.proto.context_pb2 import SliceStatusEnum +from common.tools.object_factory.Context import json_context_id def get_slice_uuid(a_endpoint_id : Dict, z_endpoint_id : Dict) -> str: return 'slc:{:s}/{:s}=={:s}/{:s}'.format( @@ -30,13 +32,13 @@ def json_slice_owner(owner_uuid : str, owner_string : str) -> Dict: return {'owner_uuid': {'uuid': owner_uuid}, 'owner_string': owner_string} def json_slice( - slice_uuid : str, context_id : Optional[Dict] = None, + slice_uuid : str, context_uuid : str = DEFAULT_CONTEXT_UUID, status : SliceStatusEnum = SliceStatusEnum.SLICESTATUS_PLANNED, endpoint_ids : List[Dict] = [], constraints : List[Dict] = [], config_rules : List[Dict] = [], service_ids : List[Dict] = [], subslice_ids : List[Dict] = [], owner : Optional[Dict] = None): result = { - 'slice_id' : json_slice_id(slice_uuid, context_id=context_id), + 'slice_id' : json_slice_id(slice_uuid, context_id=json_context_id(context_uuid)), 'slice_status' : {'slice_status': status}, 'slice_endpoint_ids': copy.deepcopy(endpoint_ids), 'slice_constraints' : copy.deepcopy(constraints), -- GitLab From d76b93e3921d652673ffaa56b535dd8512b0fa9d Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sun, 18 Dec 2022 20:29:26 +0000 Subject: [PATCH 096/353] Tools: - removed incomplete DLT load gen tool --- src/tests/tools/dlt_load_gen/__init__.py | 14 -- src/tests/tools/dlt_load_gen/__main__.py | 82 ------- src/tests/tools/dlt_load_gen/deploy_specs.sh | 26 -- src/tests/tools/dlt_load_gen/descriptors.json | 229 ------------------ src/tests/tools/dlt_load_gen/run.sh | 17 -- 5 files changed, 368 deletions(-) delete mode 100644 src/tests/tools/dlt_load_gen/__init__.py delete mode 100644 src/tests/tools/dlt_load_gen/__main__.py delete mode 100644 src/tests/tools/dlt_load_gen/deploy_specs.sh delete mode 100644 src/tests/tools/dlt_load_gen/descriptors.json delete mode 100755 src/tests/tools/dlt_load_gen/run.sh diff --git a/src/tests/tools/dlt_load_gen/__init__.py b/src/tests/tools/dlt_load_gen/__init__.py deleted file mode 100644 index 70a332512..000000000 --- a/src/tests/tools/dlt_load_gen/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - diff --git a/src/tests/tools/dlt_load_gen/__main__.py b/src/tests/tools/dlt_load_gen/__main__.py deleted file mode 100644 index cdad0662c..000000000 --- a/src/tests/tools/dlt_load_gen/__main__.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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 logging, sys -from common.proto.context_pb2 import DeviceId, Empty, LinkId, ServiceId, SliceId, TopologyId -from common.proto.dlt_connector_pb2 import DltDeviceId, DltLinkId, DltServiceId, DltSliceId -from common.tools.object_factory.Topology import json_topology_id -from context.client.ContextClient import ContextClient -from dlt.connector.client.DltConnectorClient import DltConnectorClient - -logging.basicConfig(level=logging.INFO) -LOGGER = logging.getLogger(__name__) - -def record_device(dlt_connector_client : DltConnectorClient, domain_id : TopologyId, device_id : DeviceId): - dlt_device_id = DltDeviceId() - dlt_device_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member - dlt_device_id.device_id.CopyFrom(device_id) # pylint: disable=no-member - dlt_connector_client.RecordDevice(dlt_device_id) - -def record_link(dlt_connector_client : DltConnectorClient, domain_id : TopologyId, link_id : LinkId): - dlt_link_id = DltLinkId() - dlt_link_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member - dlt_link_id.link_id.CopyFrom(link_id) # pylint: disable=no-member - dlt_connector_client.RecordLink(dlt_link_id) - -def record_service(dlt_connector_client : DltConnectorClient, domain_id : TopologyId, service_id : ServiceId): - dlt_service_id = DltServiceId() - dlt_service_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member - dlt_service_id.service_id.CopyFrom(service_id) # pylint: disable=no-member - dlt_connector_client.RecordService(dlt_service_id) - -def record_slice(dlt_connector_client : DltConnectorClient, domain_id : TopologyId, slice_id : SliceId): - dlt_slice_id = DltSliceId() - dlt_slice_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member - dlt_slice_id.slice_id.CopyFrom(slice_id) # pylint: disable=no-member - dlt_connector_client.RecordSlice(dlt_slice_id) - -def main(): - LOGGER.info('Starting...') - - context_client = ContextClient() - dlt_connector_client = DltConnectorClient() - - domain_id = TopologyId(**json_topology_id('dlt-perf-eval')) - - device_ids = context_client.ListDeviceIds(Empty()) - for device_id in device_ids.device_ids: - record_device(dlt_connector_client, domain_id, device_id) - - link_ids = context_client.ListLinkIds(Empty()) - for link_id in link_ids.link_ids: - record_link(dlt_connector_client, domain_id, link_id) - - context_ids = context_client.ListContextIds(Empty()) - for context_id in context_ids.context_ids: - service_ids = context_client.ListServiceIds(context_id) - for service_id in service_ids.service_ids: - record_service(dlt_connector_client, domain_id, service_id) - - slice_ids = context_client.ListSliceIds(context_id) - for slice_id in slice_ids.slice_ids: - record_slice(dlt_connector_client, domain_id, slice_id) - - LOGGER.info('Done!') - return 0 - -if __name__ == '__main__': - sys.exit(main()) - - - diff --git a/src/tests/tools/dlt_load_gen/deploy_specs.sh b/src/tests/tools/dlt_load_gen/deploy_specs.sh deleted file mode 100644 index 1982ef227..000000000 --- a/src/tests/tools/dlt_load_gen/deploy_specs.sh +++ /dev/null @@ -1,26 +0,0 @@ -# Set the URL of your local Docker registry where the images will be uploaded to. -export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" - -# Set the list of components, separated by spaces, you want to build images for, and deploy. -# Supported components are: -# context device automation policy service compute monitoring webui -# interdomain slice pathcomp dlt -# dbscanserving opticalattackmitigator opticalattackdetector -# l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector -export TFS_COMPONENTS="context device pathcomp service slice webui dlt" # automation monitoring compute - -# Set the tag you want to use for your images. -export TFS_IMAGE_TAG="dev" - -# Set the name of the Kubernetes namespace to deploy to. -export TFS_K8S_NAMESPACE="tfs" - -# Set additional manifest files to be applied after the deployment -export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml manifests/servicemonitors.yaml" - -# Set the new Grafana admin password -export TFS_GRAFANA_PASSWORD="admin123+" - -# If not already set, disable skip-build flag. -# If TFS_SKIP_BUILD is "YES", the containers are not rebuilt-retagged-repushed and existing ones are used. -export TFS_SKIP_BUILD="NO" #${TFS_SKIP_BUILD:-"YES"} diff --git a/src/tests/tools/dlt_load_gen/descriptors.json b/src/tests/tools/dlt_load_gen/descriptors.json deleted file mode 100644 index 5fb0c0867..000000000 --- a/src/tests/tools/dlt_load_gen/descriptors.json +++ /dev/null @@ -1,229 +0,0 @@ -{ - "contexts": [ - { - "context_id": {"context_uuid": {"uuid": "admin"}}, - "topology_ids": [], "service_ids": [] - } - ], - "topologies": [ - { - "topology_id": { - "context_id": {"context_uuid": {"uuid": "admin"}}, - "topology_uuid": {"uuid": "admin"} - }, - "device_ids": [ - {"device_uuid": {"uuid": "R1"}}, - {"device_uuid": {"uuid": "R2"}}, - {"device_uuid": {"uuid": "R3"}}, - {"device_uuid": {"uuid": "R4"}}, - {"device_uuid": {"uuid": "R5"}}, - {"device_uuid": {"uuid": "R6"}}, - {"device_uuid": {"uuid": "R7"}} - ], - "link_ids": [ - {"link_uuid": {"uuid": "R1==R2"}}, - {"link_uuid": {"uuid": "R2==R3"}}, - {"link_uuid": {"uuid": "R3==R4"}}, - {"link_uuid": {"uuid": "R4==R5"}}, - {"link_uuid": {"uuid": "R5==R6"}}, - {"link_uuid": {"uuid": "R6==R1"}}, - {"link_uuid": {"uuid": "R1==R7"}}, - {"link_uuid": {"uuid": "R3==R7"}}, - {"link_uuid": {"uuid": "R5==R7"}} - ] - } - ], - "devices": [ - { - "device_id": {"device_uuid": {"uuid": "R1"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ - {"sample_types": [], "type": "copper", "uuid": "1/1"}, - {"sample_types": [], "type": "copper", "uuid": "1/2"}, - {"sample_types": [], "type": "copper", "uuid": "1/3"}, - {"sample_types": [], "type": "copper", "uuid": "1/4"}, - {"sample_types": [], "type": "copper", "uuid": "1/5"}, - {"sample_types": [], "type": "copper", "uuid": "1/6"}, - {"sample_types": [], "type": "copper", "uuid": "2/1"}, - {"sample_types": [], "type": "copper", "uuid": "2/2"}, - {"sample_types": [], "type": "copper", "uuid": "2/3"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R2"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ - {"sample_types": [], "type": "copper", "uuid": "1/1"}, - {"sample_types": [], "type": "copper", "uuid": "1/2"}, - {"sample_types": [], "type": "copper", "uuid": "1/3"}, - {"sample_types": [], "type": "copper", "uuid": "1/4"}, - {"sample_types": [], "type": "copper", "uuid": "1/5"}, - {"sample_types": [], "type": "copper", "uuid": "1/6"}, - {"sample_types": [], "type": "copper", "uuid": "2/1"}, - {"sample_types": [], "type": "copper", "uuid": "2/2"}, - {"sample_types": [], "type": "copper", "uuid": "2/3"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R3"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ - {"sample_types": [], "type": "copper", "uuid": "1/1"}, - {"sample_types": [], "type": "copper", "uuid": "1/2"}, - {"sample_types": [], "type": "copper", "uuid": "1/3"}, - {"sample_types": [], "type": "copper", "uuid": "1/4"}, - {"sample_types": [], "type": "copper", "uuid": "1/5"}, - {"sample_types": [], "type": "copper", "uuid": "1/6"}, - {"sample_types": [], "type": "copper", "uuid": "2/1"}, - {"sample_types": [], "type": "copper", "uuid": "2/2"}, - {"sample_types": [], "type": "copper", "uuid": "2/3"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R4"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ - {"sample_types": [], "type": "copper", "uuid": "1/1"}, - {"sample_types": [], "type": "copper", "uuid": "1/2"}, - {"sample_types": [], "type": "copper", "uuid": "1/3"}, - {"sample_types": [], "type": "copper", "uuid": "1/4"}, - {"sample_types": [], "type": "copper", "uuid": "1/5"}, - {"sample_types": [], "type": "copper", "uuid": "1/6"}, - {"sample_types": [], "type": "copper", "uuid": "2/1"}, - {"sample_types": [], "type": "copper", "uuid": "2/2"}, - {"sample_types": [], "type": "copper", "uuid": "2/3"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R5"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ - {"sample_types": [], "type": "copper", "uuid": "1/1"}, - {"sample_types": [], "type": "copper", "uuid": "1/2"}, - {"sample_types": [], "type": "copper", "uuid": "1/3"}, - {"sample_types": [], "type": "copper", "uuid": "1/4"}, - {"sample_types": [], "type": "copper", "uuid": "1/5"}, - {"sample_types": [], "type": "copper", "uuid": "1/6"}, - {"sample_types": [], "type": "copper", "uuid": "2/1"}, - {"sample_types": [], "type": "copper", "uuid": "2/2"}, - {"sample_types": [], "type": "copper", "uuid": "2/3"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R6"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ - {"sample_types": [], "type": "copper", "uuid": "1/1"}, - {"sample_types": [], "type": "copper", "uuid": "1/2"}, - {"sample_types": [], "type": "copper", "uuid": "1/3"}, - {"sample_types": [], "type": "copper", "uuid": "1/4"}, - {"sample_types": [], "type": "copper", "uuid": "1/5"}, - {"sample_types": [], "type": "copper", "uuid": "1/6"}, - {"sample_types": [], "type": "copper", "uuid": "2/1"}, - {"sample_types": [], "type": "copper", "uuid": "2/2"}, - {"sample_types": [], "type": "copper", "uuid": "2/3"} - ]}}} - ]} - }, - { - "device_id": {"device_uuid": {"uuid": "R7"}}, "device_type": "emu-packet-router", "device_drivers": [0], - "device_endpoints": [], "device_operational_status": 1, "device_config": {"config_rules": [ - {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "127.0.0.1"}}, - {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "0"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": {"endpoints": [ - {"sample_types": [], "type": "copper", "uuid": "1/1"}, - {"sample_types": [], "type": "copper", "uuid": "1/2"}, - {"sample_types": [], "type": "copper", "uuid": "1/3"}, - {"sample_types": [], "type": "copper", "uuid": "2/1"}, - {"sample_types": [], "type": "copper", "uuid": "2/2"}, - {"sample_types": [], "type": "copper", "uuid": "2/3"}, - {"sample_types": [], "type": "copper", "uuid": "2/4"}, - {"sample_types": [], "type": "copper", "uuid": "2/5"}, - {"sample_types": [], "type": "copper", "uuid": "2/6"} - ]}}} - ]} - } - ], - "links": [ - { - "link_id": {"link_uuid": {"uuid": "R1==R2"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/1"}}, - {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "2/2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R2==R3"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R2"}}, "endpoint_uuid": {"uuid": "2/1"}}, - {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R3==R4"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/1"}}, - {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "2/2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R4==R5"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R4"}}, "endpoint_uuid": {"uuid": "2/1"}}, - {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R5==R6"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/1"}}, - {"device_id": {"device_uuid": {"uuid": "R6"}}, "endpoint_uuid": {"uuid": "2/2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R6==R1"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R6"}}, "endpoint_uuid": {"uuid": "2/1"}}, - {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/2"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R1==R7"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R1"}}, "endpoint_uuid": {"uuid": "2/3"}}, - {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/1"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R3==R7"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R3"}}, "endpoint_uuid": {"uuid": "2/3"}}, - {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/3"}} - ] - }, - { - "link_id": {"link_uuid": {"uuid": "R5==R7"}}, - "link_endpoint_ids": [ - {"device_id": {"device_uuid": {"uuid": "R5"}}, "endpoint_uuid": {"uuid": "2/3"}}, - {"device_id": {"device_uuid": {"uuid": "R7"}}, "endpoint_uuid": {"uuid": "2/5"}} - ] - } - ] -} \ No newline at end of file diff --git a/src/tests/tools/dlt_load_gen/run.sh b/src/tests/tools/dlt_load_gen/run.sh deleted file mode 100755 index 6329b5434..000000000 --- a/src/tests/tools/dlt_load_gen/run.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) -# -# 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. - -source tfs_runtime_env_vars.sh -python -m tests.tools.dlt_load_gen -- GitLab From ff7d18791ec5e2d830e4adbdc7bb9cdaf5912a73 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sun, 18 Dec 2022 20:30:51 +0000 Subject: [PATCH 097/353] Proto: - DLT connector: added flag to delete records --- proto/dlt_connector.proto | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/proto/dlt_connector.proto b/proto/dlt_connector.proto index 1038d6ccd..cee0c70bd 100644 --- a/proto/dlt_connector.proto +++ b/proto/dlt_connector.proto @@ -35,20 +35,24 @@ service DltConnectorService { message DltDeviceId { context.TopologyId topology_id = 1; - context.DeviceId device_id = 2; + context.DeviceId device_id = 2; + bool delete = 3; } message DltLinkId { context.TopologyId topology_id = 1; - context.LinkId link_id = 2; + context.LinkId link_id = 2; + bool delete = 3; } message DltServiceId { context.TopologyId topology_id = 1; - context.ServiceId service_id = 2; + context.ServiceId service_id = 2; + bool delete = 3; } message DltSliceId { context.TopologyId topology_id = 1; - context.SliceId slice_id = 2; + context.SliceId slice_id = 2; + bool delete = 3; } -- GitLab From c56e0defbfc1bba3e61733876db3ccbc3d1e9e3b Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sun, 18 Dec 2022 20:30:56 +0000 Subject: [PATCH 098/353] DLT connector: - factorized recording methods - implemented delete of record --- .../DltConnectorServiceServicerImpl.py | 172 +++++++----------- 1 file changed, 69 insertions(+), 103 deletions(-) diff --git a/src/dlt/connector/service/DltConnectorServiceServicerImpl.py b/src/dlt/connector/service/DltConnectorServiceServicerImpl.py index 7e07912b6..9af1ae6ea 100644 --- a/src/dlt/connector/service/DltConnectorServiceServicerImpl.py +++ b/src/dlt/connector/service/DltConnectorServiceServicerImpl.py @@ -13,8 +13,9 @@ # limitations under the License. import grpc, logging +from typing import Optional from common.method_wrappers.Decorator import MetricsPool, safe_and_metered_rpc_method -from common.proto.context_pb2 import DeviceId, Empty, LinkId, ServiceId, SliceId, TopologyId +from common.proto.context_pb2 import Empty, TopologyId from common.proto.dlt_connector_pb2 import DltDeviceId, DltLinkId, DltServiceId, DltSliceId from common.proto.dlt_connector_pb2_grpc import DltConnectorServiceServicer from common.proto.dlt_gateway_pb2 import DltRecord, DltRecordId, DltRecordOperationEnum, DltRecordTypeEnum @@ -42,34 +43,15 @@ class DltConnectorServiceServicerImpl(DltConnectorServiceServicer): @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordDevice(self, request : DltDeviceId, context : grpc.ServicerContext) -> Empty: - context_client = ContextClient() - device = context_client.GetDevice(request.device_id) - - dltgateway_client = DltGatewayClient() - - dlt_record_id = DltRecordId() - dlt_record_id.domain_uuid.uuid = request.topology_id.topology_uuid.uuid - dlt_record_id.type = DltRecordTypeEnum.DLTRECORDTYPE_DEVICE - dlt_record_id.record_uuid.uuid = device.device_id.device_uuid.uuid - - LOGGER.info('[RecordDevice] sent dlt_record_id = {:s}'.format(grpc_message_to_json_string(dlt_record_id))) - dlt_record = dltgateway_client.GetFromDlt(dlt_record_id) - LOGGER.info('[RecordDevice] recv dlt_record = {:s}'.format(grpc_message_to_json_string(dlt_record))) - - exists = record_exists(dlt_record) - LOGGER.info('[RecordDevice] exists = {:s}'.format(str(exists))) - - dlt_record = DltRecord() - dlt_record.record_id.CopyFrom(dlt_record_id) - dlt_record.operation = \ - DltRecordOperationEnum.DLTRECORDOPERATION_UPDATE \ - if exists else \ - DltRecordOperationEnum.DLTRECORDOPERATION_ADD - - dlt_record.data_json = grpc_message_to_json_string(device) - LOGGER.info('[RecordDevice] sent dlt_record = {:s}'.format(grpc_message_to_json_string(dlt_record))) - dlt_record_status = dltgateway_client.RecordToDlt(dlt_record) - LOGGER.info('[RecordDevice] recv dlt_record_status = {:s}'.format(grpc_message_to_json_string(dlt_record_status))) + data_json = None + if not request.delete: + context_client = ContextClient() + device = context_client.GetDevice(request.device_id) + data_json = grpc_message_to_json_string(device) + + self._record_entity( + request.topology_id.topology_uuid.uuid, DltRecordTypeEnum.DLTRECORDTYPE_DEVICE, + request.device_id.device_uuid.uuid, request.delete, data_json) return Empty() @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) @@ -78,34 +60,15 @@ class DltConnectorServiceServicerImpl(DltConnectorServiceServicer): @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordLink(self, request : DltLinkId, context : grpc.ServicerContext) -> Empty: - context_client = ContextClient() - link = context_client.GetLink(request.link_id) - - dltgateway_client = DltGatewayClient() - - dlt_record_id = DltRecordId() - dlt_record_id.domain_uuid.uuid = request.topology_id.topology_uuid.uuid - dlt_record_id.type = DltRecordTypeEnum.DLTRECORDTYPE_LINK - dlt_record_id.record_uuid.uuid = link.link_id.link_uuid.uuid - - LOGGER.info('[RecordLink] sent dlt_record_id = {:s}'.format(grpc_message_to_json_string(dlt_record_id))) - dlt_record = dltgateway_client.GetFromDlt(dlt_record_id) - LOGGER.info('[RecordLink] recv dlt_record = {:s}'.format(grpc_message_to_json_string(dlt_record))) - - exists = record_exists(dlt_record) - LOGGER.info('[RecordLink] exists = {:s}'.format(str(exists))) - - dlt_record = DltRecord() - dlt_record.record_id.CopyFrom(dlt_record_id) - dlt_record.operation = \ - DltRecordOperationEnum.DLTRECORDOPERATION_UPDATE \ - if exists else \ - DltRecordOperationEnum.DLTRECORDOPERATION_ADD - - dlt_record.data_json = grpc_message_to_json_string(link) - LOGGER.info('[RecordLink] sent dlt_record = {:s}'.format(grpc_message_to_json_string(dlt_record))) - dlt_record_status = dltgateway_client.RecordToDlt(dlt_record) - LOGGER.info('[RecordLink] recv dlt_record_status = {:s}'.format(grpc_message_to_json_string(dlt_record_status))) + data_json = None + if not request.delete: + context_client = ContextClient() + link = context_client.GetLink(request.link_id) + data_json = grpc_message_to_json_string(link) + + self._record_entity( + request.topology_id.topology_uuid.uuid, DltRecordTypeEnum.DLTRECORDTYPE_LINK, + request.link_id.link_uuid.uuid, request.delete, data_json) return Empty() @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) @@ -114,34 +77,15 @@ class DltConnectorServiceServicerImpl(DltConnectorServiceServicer): @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordService(self, request : DltServiceId, context : grpc.ServicerContext) -> Empty: - context_client = ContextClient() - service = context_client.GetService(request.service_id) - - dltgateway_client = DltGatewayClient() - - dlt_record_id = DltRecordId() - dlt_record_id.domain_uuid.uuid = request.topology_id.topology_uuid.uuid - dlt_record_id.type = DltRecordTypeEnum.DLTRECORDTYPE_SERVICE - dlt_record_id.record_uuid.uuid = service.service_id.service_uuid.uuid - - LOGGER.info('[RecordService] sent dlt_record_id = {:s}'.format(grpc_message_to_json_string(dlt_record_id))) - dlt_record = dltgateway_client.GetFromDlt(dlt_record_id) - LOGGER.info('[RecordService] recv dlt_record = {:s}'.format(grpc_message_to_json_string(dlt_record))) - - exists = record_exists(dlt_record) - LOGGER.info('[RecordService] exists = {:s}'.format(str(exists))) - - dlt_record = DltRecord() - dlt_record.record_id.CopyFrom(dlt_record_id) - dlt_record.operation = \ - DltRecordOperationEnum.DLTRECORDOPERATION_UPDATE \ - if exists else \ - DltRecordOperationEnum.DLTRECORDOPERATION_ADD - - dlt_record.data_json = grpc_message_to_json_string(service) - LOGGER.info('[RecordService] sent dlt_record = {:s}'.format(grpc_message_to_json_string(dlt_record))) - dlt_record_status = dltgateway_client.RecordToDlt(dlt_record) - LOGGER.info('[RecordService] recv dlt_record_status = {:s}'.format(grpc_message_to_json_string(dlt_record_status))) + data_json = None + if not request.delete: + context_client = ContextClient() + service = context_client.GetService(request.service_id) + data_json = grpc_message_to_json_string(service) + + self._record_entity( + request.topology_id.topology_uuid.uuid, DltRecordTypeEnum.DLTRECORDTYPE_SERVICE, + request.service_id.service_uuid.uuid, request.delete, data_json) return Empty() @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) @@ -150,32 +94,54 @@ class DltConnectorServiceServicerImpl(DltConnectorServiceServicer): @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def RecordSlice(self, request : DltSliceId, context : grpc.ServicerContext) -> Empty: - context_client = ContextClient() - slice_ = context_client.GetSlice(request.slice_id) + data_json = None + if not request.delete: + context_client = ContextClient() + slice_ = context_client.GetSlice(request.slice_id) + data_json = grpc_message_to_json_string(slice_) + + self._record_entity( + request.topology_id.topology_uuid.uuid, DltRecordTypeEnum.DLTRECORDTYPE_SLICE, + request.slice_id.slice_uuid.uuid, request.delete, data_json) + return Empty() + def _record_entity( + self, dlt_domain_uuid : str, dlt_record_type : DltRecordTypeEnum, dlt_record_uuid : str, delete : bool, + data_json : Optional[str] = None + ) -> None: dltgateway_client = DltGatewayClient() dlt_record_id = DltRecordId() - dlt_record_id.domain_uuid.uuid = request.topology_id.topology_uuid.uuid - dlt_record_id.type = DltRecordTypeEnum.DLTRECORDTYPE_SLICE - dlt_record_id.record_uuid.uuid = slice_.slice_id.slice_uuid.uuid + dlt_record_id.domain_uuid.uuid = dlt_domain_uuid # pylint: disable=no-member + dlt_record_id.type = dlt_record_type + dlt_record_id.record_uuid.uuid = dlt_record_uuid # pylint: disable=no-member - LOGGER.info('[RecordSlice] sent dlt_record_id = {:s}'.format(grpc_message_to_json_string(dlt_record_id))) + str_dlt_record_id = grpc_message_to_json_string(dlt_record_id) + LOGGER.debug('[_record_entity] sent dlt_record_id = {:s}'.format(str_dlt_record_id)) dlt_record = dltgateway_client.GetFromDlt(dlt_record_id) - LOGGER.info('[RecordSlice] recv dlt_record = {:s}'.format(grpc_message_to_json_string(dlt_record))) + str_dlt_record = grpc_message_to_json_string(dlt_record) + LOGGER.debug('[_record_entity] recv dlt_record = {:s}'.format(str_dlt_record)) exists = record_exists(dlt_record) - LOGGER.info('[RecordSlice] exists = {:s}'.format(str(exists))) + LOGGER.debug('[_record_entity] exists = {:s}'.format(str(exists))) dlt_record = DltRecord() - dlt_record.record_id.CopyFrom(dlt_record_id) - dlt_record.operation = \ - DltRecordOperationEnum.DLTRECORDOPERATION_UPDATE \ - if exists else \ - DltRecordOperationEnum.DLTRECORDOPERATION_ADD - - dlt_record.data_json = grpc_message_to_json_string(slice_) - LOGGER.info('[RecordSlice] sent dlt_record = {:s}'.format(grpc_message_to_json_string(dlt_record))) + dlt_record.record_id.CopyFrom(dlt_record_id) # pylint: disable=no-member + if delete and exists: + dlt_record.operation = DltRecordOperationEnum.DLTRECORDOPERATION_DELETE + elif not delete and exists: + dlt_record.operation = DltRecordOperationEnum.DLTRECORDOPERATION_UPDATE + if data_json is None: raise Exception('data_json must be provided when updating') + dlt_record.data_json = data_json + elif not delete and not exists: + dlt_record.operation = DltRecordOperationEnum.DLTRECORDOPERATION_ADD + if data_json is None: raise Exception('data_json must be provided when adding') + dlt_record.data_json = data_json + else: + return + + str_dlt_record = grpc_message_to_json_string(dlt_record) + LOGGER.debug('[_record_entity] sent dlt_record = {:s}'.format(str_dlt_record)) dlt_record_status = dltgateway_client.RecordToDlt(dlt_record) - LOGGER.info('[RecordSlice] recv dlt_record_status = {:s}'.format(grpc_message_to_json_string(dlt_record_status))) - return Empty() + str_dlt_record_status = grpc_message_to_json_string(dlt_record_status) + LOGGER.debug('[_record_entity] recv dlt_record_status = {:s}'.format(str_dlt_record_status)) -- GitLab From 65a8011b8909db3d5ed5f39da07d5fbb45cb0c69 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sun, 18 Dec 2022 20:32:01 +0000 Subject: [PATCH 099/353] Tools - load-gen: - added support to record load-gen operations in DLT to take performance evaluation metrics - improved mixed request support --- src/tests/tools/load_gen/Constants.py | 18 ++- src/tests/tools/load_gen/DltTools.py | 123 +++++++++++++++++++ src/tests/tools/load_gen/Parameters.py | 17 ++- src/tests/tools/load_gen/RequestGenerator.py | 46 ++++--- src/tests/tools/load_gen/RequestScheduler.py | 112 ++++++++++++----- src/tests/tools/load_gen/__main__.py | 18 +-- src/tests/tools/load_gen/deploy_specs.sh | 2 +- 7 files changed, 268 insertions(+), 68 deletions(-) create mode 100644 src/tests/tools/load_gen/DltTools.py diff --git a/src/tests/tools/load_gen/Constants.py b/src/tests/tools/load_gen/Constants.py index 94d80bfdf..1eeb62686 100644 --- a/src/tests/tools/load_gen/Constants.py +++ b/src/tests/tools/load_gen/Constants.py @@ -12,8 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -REQUEST_TYPE_SERVICE_L2NM = 'svc-l2nm' -REQUEST_TYPE_SERVICE_L3NM = 'svc-l3nm' -REQUEST_TYPE_SERVICE_TAPI = 'svc-tapi' -REQUEST_TYPE_SLICE_L2NM = 'slc-l2nm' -REQUEST_TYPE_SLICE_L3NM = 'slc-l3nm' +from enum import Enum + +class RequestType(Enum): + SERVICE_L2NM = 'svc-l2nm' + SERVICE_L3NM = 'svc-l3nm' + SERVICE_TAPI = 'svc-tapi' + SLICE_L2NM = 'slc-l2nm' + SLICE_L3NM = 'slc-l3nm' + +ENDPOINT_COMPATIBILITY = { + 'PHOTONIC_MEDIA:FLEX:G_6_25GHZ:INPUT': 'PHOTONIC_MEDIA:FLEX:G_6_25GHZ:OUTPUT', + 'PHOTONIC_MEDIA:DWDM:G_50GHZ:INPUT' : 'PHOTONIC_MEDIA:DWDM:G_50GHZ:OUTPUT', +} diff --git a/src/tests/tools/load_gen/DltTools.py b/src/tests/tools/load_gen/DltTools.py new file mode 100644 index 000000000..34d195ad7 --- /dev/null +++ b/src/tests/tools/load_gen/DltTools.py @@ -0,0 +1,123 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 json, queue +from typing import Optional, Set, Tuple +from common.proto.context_pb2 import DeviceId, LinkId, ServiceId, SliceId, TopologyId +from common.proto.dlt_connector_pb2 import DltDeviceId, DltLinkId, DltServiceId, DltSliceId +from common.tools.grpc.Tools import grpc_message_to_json_string +from common.tools.object_factory.Topology import json_topology_id +from context.client.ContextClient import ContextClient +from dlt.connector.client.DltConnectorClient import DltConnectorClient + +def explore_entities_to_record( + slice_id : Optional[SliceId] = None, service_id : Optional[ServiceId] = None +) -> Tuple[Set[str], Set[str], Set[str]]: + + context_client = ContextClient() + + slices_to_record : Set[str] = set() + services_to_record : Set[str] = set() + devices_to_record : Set[str] = set() + + slices_to_explore = queue.Queue() + services_to_explore = queue.Queue() + if slice_id is not None: slices_to_explore.put(slice_id) + if service_id is not None: services_to_explore.put(service_id) + + while not slices_to_explore.empty(): + slice_id = slices_to_explore.get() + slices_to_record.add(grpc_message_to_json_string(slice_id)) + + slice_ = context_client.GetSlice(slice_id) + + for endpoint_id in slice_.slice_endpoint_ids: + devices_to_record.add(grpc_message_to_json_string(endpoint_id.device_id)) + for subslice_id in slice_.slice_subslice_ids: + slices_to_explore.put(subslice_id) + for service_id in slice_.slice_service_ids: + services_to_explore.put(service_id) + + while not services_to_explore.empty(): + service_id = services_to_explore.get() + services_to_record.add(grpc_message_to_json_string(service_id)) + + service = context_client.GetService(service_id) + + for endpoint_id in service.service_endpoint_ids: + devices_to_record.add(grpc_message_to_json_string(endpoint_id.device_id)) + + connections = context_client.ListConnections(service_id) + for connection in connections.connections: + for endpoint_id in connection.path_hops_endpoint_ids: + devices_to_record.add(grpc_message_to_json_string(endpoint_id.device_id)) + for service_id in connection.sub_service_ids: + services_to_explore.put(service_id) + + return slices_to_record, services_to_record, devices_to_record + +def record_device_to_dlt( + dlt_connector_client : DltConnectorClient, domain_id : TopologyId, device_id : DeviceId, delete : bool = False +) -> None: + dlt_device_id = DltDeviceId() + dlt_device_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member + dlt_device_id.device_id.CopyFrom(device_id) # pylint: disable=no-member + dlt_device_id.delete = delete + dlt_connector_client.RecordDevice(dlt_device_id) + +def record_link_to_dlt( + dlt_connector_client : DltConnectorClient, domain_id : TopologyId, link_id : LinkId, delete : bool = False +) -> None: + dlt_link_id = DltLinkId() + dlt_link_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member + dlt_link_id.link_id.CopyFrom(link_id) # pylint: disable=no-member + dlt_link_id.delete = delete + dlt_connector_client.RecordLink(dlt_link_id) + +def record_service_to_dlt( + dlt_connector_client : DltConnectorClient, domain_id : TopologyId, service_id : ServiceId, delete : bool = False +) -> None: + dlt_service_id = DltServiceId() + dlt_service_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member + dlt_service_id.service_id.CopyFrom(service_id) # pylint: disable=no-member + dlt_service_id.delete = delete + dlt_connector_client.RecordService(dlt_service_id) + +def record_slice_to_dlt( + dlt_connector_client : DltConnectorClient, domain_id : TopologyId, slice_id : SliceId, delete : bool = False +) -> None: + dlt_slice_id = DltSliceId() + dlt_slice_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member + dlt_slice_id.slice_id.CopyFrom(slice_id) # pylint: disable=no-member + dlt_slice_id.delete = delete + dlt_connector_client.RecordSlice(dlt_slice_id) + +def record_entities( + slices_to_record : Set[str] = set(), services_to_record : Set[str] = set(), devices_to_record : Set[str] = set(), + delete : bool = False +) -> None: + dlt_connector_client = DltConnectorClient() + dlt_domain_id = TopologyId(**json_topology_id('dlt-perf-eval')) + + for str_device_id in devices_to_record: + device_id = DeviceId(**(json.loads(str_device_id))) + record_device_to_dlt(dlt_connector_client, dlt_domain_id, device_id, delete=delete) + + for str_service_id in services_to_record: + service_id = ServiceId(**(json.loads(str_service_id))) + record_service_to_dlt(dlt_connector_client, dlt_domain_id, service_id, delete=delete) + + for str_slice_id in slices_to_record: + slice_id = SliceId(**(json.loads(str_slice_id))) + record_slice_to_dlt(dlt_connector_client, dlt_domain_id, slice_id, delete=delete) diff --git a/src/tests/tools/load_gen/Parameters.py b/src/tests/tools/load_gen/Parameters.py index 9aab3b147..c74d18248 100644 --- a/src/tests/tools/load_gen/Parameters.py +++ b/src/tests/tools/load_gen/Parameters.py @@ -18,7 +18,7 @@ class Parameters: def __init__( self, num_requests : int, request_types : List[str], offered_load : Optional[float] = None, inter_arrival_time : Optional[float] = None, holding_time : Optional[float] = None, - dry_mode : bool = False + dry_mode : bool = False, record_to_dlt : bool = False, dlt_domain_id : Optional[str] = None ) -> None: self._num_requests = num_requests self._request_types = request_types @@ -26,6 +26,8 @@ class Parameters: self._inter_arrival_time = inter_arrival_time self._holding_time = holding_time self._dry_mode = dry_mode + self._record_to_dlt = record_to_dlt + self._dlt_domain_id = dlt_domain_id if self._offered_load is None and self._holding_time is not None and self._inter_arrival_time is not None: self._offered_load = self._holding_time / self._inter_arrival_time @@ -33,6 +35,13 @@ class Parameters: self._inter_arrival_time = self._holding_time / self._offered_load elif self._offered_load is not None and self._holding_time is None and self._inter_arrival_time is not None: self._holding_time = self._offered_load * self._inter_arrival_time + else: + MSG = 'Exactly two of offered_load({:s}), inter_arrival_time({:s}), holding_time({:s}) must be specified.' + raise Exception(MSG.format(str(self._offered_load), str(self._inter_arrival_time), str(self._holding_time))) + + if self._record_to_dlt and self._dlt_domain_id is None: + MSG = 'Parameter dlt_domain_id({:s}) must be specified with record_to_dlt({:s}).' + raise Exception(MSG.format(str(self._dlt_domain_id), str(self._record_to_dlt))) @property def num_requests(self): return self._num_requests @@ -51,3 +60,9 @@ class Parameters: @property def dry_mode(self): return self._dry_mode + + @property + def record_to_dlt(self): return self._record_to_dlt + + @property + def dlt_domain_id(self): return self._dlt_domain_id diff --git a/src/tests/tools/load_gen/RequestGenerator.py b/src/tests/tools/load_gen/RequestGenerator.py index 6d0be9d9c..b0ec9bea6 100644 --- a/src/tests/tools/load_gen/RequestGenerator.py +++ b/src/tests/tools/load_gen/RequestGenerator.py @@ -14,7 +14,7 @@ import logging, json, random, threading from typing import Dict, Optional, Set, Tuple -from common.proto.context_pb2 import Empty +from common.proto.context_pb2 import Empty, TopologyId from common.tools.object_factory.Constraint import json_constraint_custom from common.tools.object_factory.ConfigRule import json_config_rule_set from common.tools.object_factory.Device import json_device_id @@ -22,19 +22,15 @@ from common.tools.object_factory.EndPoint import json_endpoint_id from common.tools.object_factory.Service import ( json_service_l2nm_planned, json_service_l3nm_planned, json_service_tapi_planned) from common.tools.object_factory.Slice import json_slice +from common.tools.object_factory.Topology import json_topology_id from context.client.ContextClient import ContextClient -from .Constants import ( - REQUEST_TYPE_SERVICE_L2NM, REQUEST_TYPE_SERVICE_L3NM, REQUEST_TYPE_SERVICE_TAPI, - REQUEST_TYPE_SLICE_L2NM, REQUEST_TYPE_SLICE_L3NM) +from dlt.connector.client.DltConnectorClient import DltConnectorClient +from tests.tools.load_gen.DltTools import record_device_to_dlt, record_link_to_dlt +from .Constants import ENDPOINT_COMPATIBILITY, RequestType from .Parameters import Parameters LOGGER = logging.getLogger(__name__) -ENDPOINT_COMPATIBILITY = { - 'PHOTONIC_MEDIA:FLEX:G_6_25GHZ:INPUT': 'PHOTONIC_MEDIA:FLEX:G_6_25GHZ:OUTPUT', - 'PHOTONIC_MEDIA:DWDM:G_50GHZ:INPUT' : 'PHOTONIC_MEDIA:DWDM:G_50GHZ:OUTPUT', -} - class RequestGenerator: def __init__(self, parameters : Parameters) -> None: self._parameters = parameters @@ -51,6 +47,10 @@ class RequestGenerator: self._used_device_endpoints.clear() context_client = ContextClient() + dlt_connector_client = DltConnectorClient() + + if self._parameters.record_to_dlt: + dlt_domain_id = TopologyId(**json_topology_id('dlt-perf-eval')) devices = context_client.ListDevices(Empty()) for device in devices.devices: @@ -62,6 +62,9 @@ class RequestGenerator: _endpoints.add(endpoint_uuid) self._endpoint_ids_to_types.setdefault((device_uuid, endpoint_uuid), endpoint_type) self._endpoint_types_to_ids.setdefault(endpoint_type, set()).add((device_uuid, endpoint_uuid)) + + if self._parameters.record_to_dlt: + record_device_to_dlt(dlt_connector_client, dlt_domain_id, device.device_id) links = context_client.ListLinks(Empty()) for link in links.links: @@ -80,6 +83,9 @@ class RequestGenerator: endpoint_key = (device_uuid, endpoint_uuid) if endpoint_key not in endpoints_for_type: continue endpoints_for_type.discard(endpoint_key) + + if self._parameters.record_to_dlt: + record_link_to_dlt(dlt_connector_client, dlt_domain_id, link.link_id) @property def num_requests_generated(self): return self._num_requests @@ -153,14 +159,14 @@ class RequestGenerator: # choose request type request_type = random.choice(self._parameters.request_types) - if request_type in {REQUEST_TYPE_SERVICE_L2NM, REQUEST_TYPE_SERVICE_L3NM, REQUEST_TYPE_SERVICE_TAPI}: + if request_type in {RequestType.SERVICE_L2NM, RequestType.SERVICE_L3NM, RequestType.SERVICE_TAPI}: return self._compose_service(num_request, request_uuid, request_type) - elif request_type in {REQUEST_TYPE_SLICE_L2NM, REQUEST_TYPE_SLICE_L3NM}: + elif request_type in {RequestType.SLICE_L2NM, RequestType.SLICE_L3NM}: return self._compose_slice(num_request, request_uuid, request_type) def _compose_service(self, num_request : int, request_uuid : str, request_type : str) -> Optional[Dict]: # choose source endpoint - src_endpoint_types = set(ENDPOINT_COMPATIBILITY.keys()) if request_type in {REQUEST_TYPE_SERVICE_TAPI} else None + src_endpoint_types = set(ENDPOINT_COMPATIBILITY.keys()) if request_type in {RequestType.SERVICE_TAPI} else None src = self._use_device_endpoint(request_uuid, endpoint_types=src_endpoint_types) if src is None: LOGGER.warning('>> No source endpoint is available') @@ -170,10 +176,10 @@ class RequestGenerator: # identify compatible destination endpoint types src_endpoint_type = self._endpoint_ids_to_types.get((src_device_uuid,src_endpoint_uuid)) dst_endpoint_type = ENDPOINT_COMPATIBILITY.get(src_endpoint_type) - dst_endpoint_types = {dst_endpoint_type} if request_type in {REQUEST_TYPE_SERVICE_TAPI} else None + dst_endpoint_types = {dst_endpoint_type} if request_type in {RequestType.SERVICE_TAPI} else None # identify excluded destination devices - exclude_device_uuids = {} if request_type in {REQUEST_TYPE_SERVICE_TAPI} else {src_device_uuid} + exclude_device_uuids = {} if request_type in {RequestType.SERVICE_TAPI} else {src_device_uuid} # choose feasible destination endpoint dst = self._use_device_endpoint( @@ -192,7 +198,7 @@ class RequestGenerator: json_endpoint_id(json_device_id(dst_device_uuid), dst_endpoint_uuid), ] - if request_type == REQUEST_TYPE_SERVICE_L2NM: + if request_type == RequestType.SERVICE_L2NM: constraints = [ json_constraint_custom('bandwidth[gbps]', '10.0'), json_constraint_custom('latency[ms]', '20.0'), @@ -223,7 +229,7 @@ class RequestGenerator: return json_service_l2nm_planned( request_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules) - elif request_type == REQUEST_TYPE_SERVICE_L3NM: + elif request_type == RequestType.SERVICE_L3NM: constraints = [ json_constraint_custom('bandwidth[gbps]', '10.0'), json_constraint_custom('latency[ms]', '20.0'), @@ -262,7 +268,7 @@ class RequestGenerator: return json_service_l3nm_planned( request_uuid, endpoint_ids=endpoint_ids, constraints=constraints, config_rules=config_rules) - elif request_type == REQUEST_TYPE_SERVICE_TAPI: + elif request_type == RequestType.SERVICE_TAPI: config_rules = [ json_config_rule_set('/settings', { 'capacity_value' : 50.0, @@ -284,7 +290,7 @@ class RequestGenerator: src_device_uuid,src_endpoint_uuid = src # identify excluded destination devices - exclude_device_uuids = {} if request_type in {REQUEST_TYPE_SERVICE_TAPI} else {src_device_uuid} + exclude_device_uuids = {} if request_type in {RequestType.SERVICE_TAPI} else {src_device_uuid} # choose feasible destination endpoint dst = self._use_device_endpoint(request_uuid, exclude_device_uuids=exclude_device_uuids) @@ -306,7 +312,7 @@ class RequestGenerator: json_constraint_custom('latency[ms]', '20.0'), ] - if request_type == REQUEST_TYPE_SLICE_L2NM: + if request_type == RequestType.SLICE_L2NM: vlan_id = num_request % 1000 circuit_id = '{:03d}'.format(vlan_id) src_router_id = '10.0.0.{:d}'.format(int(src_device_uuid.replace('R', ''))) @@ -331,7 +337,7 @@ class RequestGenerator: }), ] - elif request_type == REQUEST_TYPE_SLICE_L3NM: + elif request_type == RequestType.SLICE_L3NM: vlan_id = num_request % 1000 bgp_as = 60000 + (num_request % 10000) bgp_route_target = '{:5d}:{:03d}'.format(bgp_as, 333) diff --git a/src/tests/tools/load_gen/RequestScheduler.py b/src/tests/tools/load_gen/RequestScheduler.py index 5be579c71..eafb95c30 100644 --- a/src/tests/tools/load_gen/RequestScheduler.py +++ b/src/tests/tools/load_gen/RequestScheduler.py @@ -13,14 +13,15 @@ # limitations under the License. import copy, logging, pytz, random -from datetime import datetime, timedelta from apscheduler.executors.pool import ThreadPoolExecutor from apscheduler.jobstores.memory import MemoryJobStore from apscheduler.schedulers.blocking import BlockingScheduler -from typing import Dict +from datetime import datetime, timedelta +from typing import Dict, Optional from common.proto.context_pb2 import Service, ServiceId, Slice, SliceId from service.client.ServiceClient import ServiceClient from slice.client.SliceClient import SliceClient +from .DltTools import explore_entities_to_record, record_entities from .Parameters import Parameters from .RequestGenerator import RequestGenerator @@ -80,16 +81,7 @@ class RequestScheduler: dst_endpoint_uuid = request['service_endpoint_ids'][1]['endpoint_uuid']['uuid'] LOGGER.info('Setup Service: uuid=%s src=%s:%s dst=%s:%s', service_uuid, src_device_uuid, src_endpoint_uuid, dst_device_uuid, dst_endpoint_uuid) - - if not self._parameters.dry_mode: - request_add = copy.deepcopy(request) - request_add['service_endpoint_ids'] = [] - request_add['service_constraints'] = [] - request_add['service_config'] = {'config_rules': []} - service_client = ServiceClient() # create instances per request to load balance between pods - service_client.CreateService(Service(**request_add)) - service_client.UpdateService(Service(**request)) - service_client.close() + self._create_update(service=request) elif 'slice_id' in request: slice_uuid = request['slice_id']['slice_uuid']['uuid'] @@ -99,16 +91,7 @@ class RequestScheduler: dst_endpoint_uuid = request['slice_endpoint_ids'][1]['endpoint_uuid']['uuid'] LOGGER.info('Setup Slice: uuid=%s src=%s:%s dst=%s:%s', slice_uuid, src_device_uuid, src_endpoint_uuid, dst_device_uuid, dst_endpoint_uuid) - - if not self._parameters.dry_mode: - request_add = copy.deepcopy(request) - request_add['slice_endpoint_ids'] = [] - request_add['slice_constraints'] = [] - request_add['slice_config'] = {'config_rules': []} - slice_client = SliceClient() # create instances per request to load balance between pods - slice_client.CreateSlice(Slice(**request_add)) - slice_client.UpdateSlice(Slice(**request)) - slice_client.close() + self._create_update(slice_=request) self._schedule_request_teardown(request) @@ -121,11 +104,7 @@ class RequestScheduler: dst_endpoint_uuid = request['service_endpoint_ids'][1]['endpoint_uuid']['uuid'] LOGGER.info('Teardown Service: uuid=%s src=%s:%s dst=%s:%s', service_uuid, src_device_uuid, src_endpoint_uuid, dst_device_uuid, dst_endpoint_uuid) - - if not self._parameters.dry_mode: - service_client = ServiceClient() # create instances per request to load balance between pods - service_client.DeleteService(ServiceId(**(request['service_id']))) - service_client.close() + self._delete(service_id=ServiceId(**(request['service_id']))) elif 'slice_id' in request: slice_uuid = request['slice_id']['slice_uuid']['uuid'] @@ -135,10 +114,79 @@ class RequestScheduler: dst_endpoint_uuid = request['slice_endpoint_ids'][1]['endpoint_uuid']['uuid'] LOGGER.info('Teardown Slice: uuid=%s src=%s:%s dst=%s:%s', slice_uuid, src_device_uuid, src_endpoint_uuid, dst_device_uuid, dst_endpoint_uuid) - - if not self._parameters.dry_mode: - slice_client = SliceClient() # create instances per request to load balance between pods - slice_client.DeleteSlice(SliceId(**(request['slice_id']))) - slice_client.close() + self._delete(slice_id=SliceId(**(request['slice_id']))) self._generator.release_request(request) + + def _create_update(self, service : Optional[Dict] = None, slice_ : Optional[Dict] = None) -> None: + if self._parameters.dry_mode: return + + service_id = None + if service is not None: + service_add = copy.deepcopy(service) + service_add['service_endpoint_ids'] = [] + service_add['service_constraints'] = [] + service_add['service_config'] = {'config_rules': []} + + service_client = ServiceClient() + service_id = service_client.CreateService(Service(**service_add)) + service_client.close() + + slice_id = None + if slice_ is not None: + slice_add = copy.deepcopy(slice_) + slice_add['slice_endpoint_ids'] = [] + slice_add['slice_constraints'] = [] + slice_add['slice_config'] = {'config_rules': []} + + slice_client = SliceClient() + slice_id = slice_client.CreateSlice(Slice(**slice_add)) + slice_client.close() + + if self._parameters.record_to_dlt: + entities_to_record = explore_entities_to_record(slice_id=slice_id, service_id=service_id) + slices_to_record, services_to_record, devices_to_record = entities_to_record + record_entities( + slices_to_record=slices_to_record, services_to_record=services_to_record, + devices_to_record=devices_to_record, delete=False) + + service_id = None + if service is not None: + service_client = ServiceClient() + service_id = service_client.UpdateService(Service(**service)) + service_client.close() + + slice_id = None + if slice_ is not None: + slice_client = SliceClient() + slice_id = slice_client.UpdateSlice(Slice(**slice_)) + slice_client.close() + + if self._parameters.record_to_dlt: + entities_to_record = explore_entities_to_record(slice_id=slice_id, service_id=service_id) + slices_to_record, services_to_record, devices_to_record = entities_to_record + record_entities( + slices_to_record=slices_to_record, services_to_record=services_to_record, + devices_to_record=devices_to_record, delete=False) + + def _delete(self, service_id : Optional[ServiceId] = None, slice_id : Optional[SliceId] = None) -> None: + if self._parameters.dry_mode: return + + if self._parameters.record_to_dlt: + entities_to_record = explore_entities_to_record(slice_id=slice_id, service_id=service_id) + slices_to_record, services_to_record, devices_to_record = entities_to_record + + if slice_id is not None: + slice_client = SliceClient() + slice_client.DeleteSlice(slice_id) + slice_client.close() + + if service_id is not None: + service_client = ServiceClient() + service_client.DeleteService(service_id) + service_client.close() + + if self._parameters.record_to_dlt: + record_entities( + slices_to_record=slices_to_record, services_to_record=services_to_record, + devices_to_record=devices_to_record, delete=True) diff --git a/src/tests/tools/load_gen/__main__.py b/src/tests/tools/load_gen/__main__.py index f5d9d364d..ca99be485 100644 --- a/src/tests/tools/load_gen/__main__.py +++ b/src/tests/tools/load_gen/__main__.py @@ -13,9 +13,7 @@ # limitations under the License. import logging, sys -from .Constants import ( - REQUEST_TYPE_SERVICE_L2NM, REQUEST_TYPE_SERVICE_L3NM, REQUEST_TYPE_SERVICE_TAPI, - REQUEST_TYPE_SLICE_L2NM, REQUEST_TYPE_SLICE_L3NM) +from .Constants import RequestType from .Parameters import Parameters from .RequestGenerator import RequestGenerator from .RequestScheduler import RequestScheduler @@ -28,15 +26,17 @@ def main(): parameters = Parameters( num_requests = 100, request_types = [ - REQUEST_TYPE_SERVICE_L2NM, - REQUEST_TYPE_SERVICE_L3NM, - REQUEST_TYPE_SERVICE_TAPI, - REQUEST_TYPE_SLICE_L2NM, - REQUEST_TYPE_SLICE_L3NM, + RequestType.SERVICE_L2NM, + RequestType.SERVICE_L3NM, + #RequestType.SERVICE_TAPI, + RequestType.SLICE_L2NM, + RequestType.SLICE_L3NM, ], offered_load = 50, holding_time = 10, - dry_mode = False, # in dry mode, no request is sent to TeraFlowSDN + dry_mode = False, # in dry mode, no request is sent to TeraFlowSDN + record_to_dlt = True, # if record_to_dlt, changes in device/link/service/slice are uploaded to DLT + dlt_domain_id = 'dlt-perf-eval', # domain used to uploaded entities, ignored when record_to_dlt = False ) LOGGER.info('Initializing Generator...') diff --git a/src/tests/tools/load_gen/deploy_specs.sh b/src/tests/tools/load_gen/deploy_specs.sh index 238918480..1982ef227 100644 --- a/src/tests/tools/load_gen/deploy_specs.sh +++ b/src/tests/tools/load_gen/deploy_specs.sh @@ -7,7 +7,7 @@ export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" # interdomain slice pathcomp dlt # dbscanserving opticalattackmitigator opticalattackdetector # l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector -export TFS_COMPONENTS="context device pathcomp service slice webui" # automation monitoring compute +export TFS_COMPONENTS="context device pathcomp service slice webui dlt" # automation monitoring compute # Set the tag you want to use for your images. export TFS_IMAGE_TAG="dev" -- GitLab From ef96918047b6e5b0ff07461682aae19bd20e6750 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Sun, 18 Dec 2022 20:40:14 +0000 Subject: [PATCH 100/353] Tools - load-gen: - disabled DLT by default --- src/tests/tools/load_gen/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/tools/load_gen/__main__.py b/src/tests/tools/load_gen/__main__.py index ca99be485..f622f4d14 100644 --- a/src/tests/tools/load_gen/__main__.py +++ b/src/tests/tools/load_gen/__main__.py @@ -35,7 +35,7 @@ def main(): offered_load = 50, holding_time = 10, dry_mode = False, # in dry mode, no request is sent to TeraFlowSDN - record_to_dlt = True, # if record_to_dlt, changes in device/link/service/slice are uploaded to DLT + record_to_dlt = False, # if record_to_dlt, changes in device/link/service/slice are uploaded to DLT dlt_domain_id = 'dlt-perf-eval', # domain used to uploaded entities, ignored when record_to_dlt = False ) -- GitLab From 533ea7b4e8588b05fa48c843ee8f2ce2f8c1538d Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Wed, 21 Dec 2022 13:39:47 +0000 Subject: [PATCH 101/353] Updated DLT functional test script --- .../tools/load_gen/test_dlt_functional.py | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/tests/tools/load_gen/test_dlt_functional.py diff --git a/src/tests/tools/load_gen/test_dlt_functional.py b/src/tests/tools/load_gen/test_dlt_functional.py new file mode 100644 index 000000000..9c6c3d5ba --- /dev/null +++ b/src/tests/tools/load_gen/test_dlt_functional.py @@ -0,0 +1,73 @@ +# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) +# +# 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 sys +from common.proto.context_pb2 import ( + DEVICEOPERATIONALSTATUS_ENABLED, Device, DeviceId, LinkId, ServiceId, SliceId, TopologyId) +from common.proto.dlt_connector_pb2 import DltDeviceId, DltLinkId, DltServiceId, DltSliceId +from common.tools.object_factory.Device import json_device +from common.tools.object_factory.Topology import json_topology_id +from context.client.ContextClient import ContextClient +from dlt.connector.client.DltConnectorClient import DltConnectorClient + +def record_device_to_dlt( + dlt_connector_client : DltConnectorClient, domain_id : TopologyId, device_id : DeviceId, delete : bool = False +) -> None: + dlt_device_id = DltDeviceId() + dlt_device_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member + dlt_device_id.device_id.CopyFrom(device_id) # pylint: disable=no-member + dlt_device_id.delete = delete + dlt_connector_client.RecordDevice(dlt_device_id) + +def record_link_to_dlt( + dlt_connector_client : DltConnectorClient, domain_id : TopologyId, link_id : LinkId, delete : bool = False +) -> None: + dlt_link_id = DltLinkId() + dlt_link_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member + dlt_link_id.link_id.CopyFrom(link_id) # pylint: disable=no-member + dlt_link_id.delete = delete + dlt_connector_client.RecordLink(dlt_link_id) + +def record_service_to_dlt( + dlt_connector_client : DltConnectorClient, domain_id : TopologyId, service_id : ServiceId, delete : bool = False +) -> None: + dlt_service_id = DltServiceId() + dlt_service_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member + dlt_service_id.service_id.CopyFrom(service_id) # pylint: disable=no-member + dlt_service_id.delete = delete + dlt_connector_client.RecordService(dlt_service_id) + +def record_slice_to_dlt( + dlt_connector_client : DltConnectorClient, domain_id : TopologyId, slice_id : SliceId, delete : bool = False +) -> None: + dlt_slice_id = DltSliceId() + dlt_slice_id.topology_id.CopyFrom(domain_id) # pylint: disable=no-member + dlt_slice_id.slice_id.CopyFrom(slice_id) # pylint: disable=no-member + dlt_slice_id.delete = delete + dlt_connector_client.RecordSlice(dlt_slice_id) + +def main(): + context_client = ContextClient() + dlt_connector_client = DltConnectorClient() + + device = Device(**json_device('test-device', 'packet-router', DEVICEOPERATIONALSTATUS_ENABLED)) + device_id = context_client.SetDevice(device) + + dlt_domain_id = TopologyId(**json_topology_id('dlt-func-test')) + record_device_to_dlt(dlt_connector_client, dlt_domain_id, device_id, delete=False) + + return 0 + +if __name__ == '__main__': + sys.exit(main()) -- GitLab From 5b1d191c9d800c7a5ab4078a4517285f78921b60 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 22 Dec 2022 08:29:33 +0000 Subject: [PATCH 102/353] Common - Load Desriptor tools: - removed incorrect instruction --- src/common/tools/descriptor/Loader.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/common/tools/descriptor/Loader.py b/src/common/tools/descriptor/Loader.py index f14e2caf6..e3fc86c05 100644 --- a/src/common/tools/descriptor/Loader.py +++ b/src/common/tools/descriptor/Loader.py @@ -250,5 +250,4 @@ class DescriptorLoader: num_ok += 1 except Exception as e: # pylint: disable=broad-except error_list.append(f'{str(entity)}: {str(e)}') - num_err += 1 self.__results.append((entity_name, action_name, num_ok, error_list)) -- GitLab From 8962a9c2cade5de6ae78deb0a0e9ac68d0125165 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 22 Dec 2022 15:35:34 +0000 Subject: [PATCH 103/353] Device component - MicroWave driver - added support for user/pass basic authentication - added HTTP schema settings - added field to filter desired underlying devices under management - corrected endpoint uuid composition - improved error handling - improved retrieval of existing services --- .../drivers/microwave/IETFApiDriver.py | 20 +++-- src/device/service/drivers/microwave/Tools.py | 82 +++++++++++-------- 2 files changed, 61 insertions(+), 41 deletions(-) diff --git a/src/device/service/drivers/microwave/IETFApiDriver.py b/src/device/service/drivers/microwave/IETFApiDriver.py index b2652f91b..3660eb719 100644 --- a/src/device/service/drivers/microwave/IETFApiDriver.py +++ b/src/device/service/drivers/microwave/IETFApiDriver.py @@ -13,6 +13,7 @@ # limitations under the License. import logging, requests, threading +from requests.auth import HTTPBasicAuth from typing import Any, Iterator, List, Optional, Tuple, Union from common.method_wrappers.Decorator import MetricsPool, metered_subclass_method from common.type_checkers.Checkers import chk_string, chk_type @@ -29,15 +30,20 @@ class IETFApiDriver(_Driver): self.__lock = threading.Lock() self.__started = threading.Event() self.__terminate = threading.Event() - self.__ietf_root = 'https://' + address + ':' + str(port) + username = settings.get('username') + password = settings.get('password') + self.__auth = HTTPBasicAuth(username, password) if username is not None and password is not None else None + scheme = settings.get('scheme', 'http') + self.__ietf_root = '{:s}://{:s}:{:d}'.format(scheme, address, int(port)) self.__timeout = int(settings.get('timeout', 120)) + self.__node_ids = set(settings.get('node_ids', [])) def Connect(self) -> bool: url = self.__ietf_root + '/nmswebs/restconf/data/ietf-network:networks' with self.__lock: if self.__started.is_set(): return True try: - requests.get(url, timeout=self.__timeout, verify=False) + requests.get(url, timeout=self.__timeout, verify=False, auth=self.__auth) except requests.exceptions.Timeout: LOGGER.exception('Timeout connecting {:s}'.format(str(self.__ietf_root))) return False @@ -67,7 +73,9 @@ class IETFApiDriver(_Driver): for i, resource_key in enumerate(resource_keys): str_resource_name = 'resource_key[#{:d}]'.format(i) chk_string(str_resource_name, resource_key, allow_empty=False) - results.extend(config_getter(self.__ietf_root, resource_key, self.__timeout)) + results.extend(config_getter( + self.__ietf_root, resource_key, timeout=self.__timeout, auth=self.__auth, + node_ids=self.__node_ids)) return results @metered_subclass_method(METRICS_POOL) @@ -87,7 +95,8 @@ class IETFApiDriver(_Driver): uuid = find_key(resource, 'uuid') data = create_connectivity_service( - self.__ietf_root, self.__timeout, uuid, node_id_src, tp_id_src, node_id_dst, tp_id_dst, vlan_id) + self.__ietf_root, uuid, node_id_src, tp_id_src, node_id_dst, tp_id_dst, vlan_id, + timeout=self.__timeout, auth=self.__auth) results.extend(data) return results @@ -99,7 +108,8 @@ class IETFApiDriver(_Driver): for resource in resources: LOGGER.info('resource = {:s}'.format(str(resource))) uuid = find_key(resource, 'uuid') - results.extend(delete_connectivity_service(self.__ietf_root, self.__timeout, uuid)) + results.extend(delete_connectivity_service( + self.__ietf_root, uuid, timeout=self.__timeout, auth=self.__auth)) return results @metered_subclass_method(METRICS_POOL) diff --git a/src/device/service/drivers/microwave/Tools.py b/src/device/service/drivers/microwave/Tools.py index 4f74def4d..a91c60af5 100644 --- a/src/device/service/drivers/microwave/Tools.py +++ b/src/device/service/drivers/microwave/Tools.py @@ -13,6 +13,8 @@ # limitations under the License. import json, logging, requests +from requests.auth import HTTPBasicAuth +from typing import Optional, Set from device.service.driver_api._Driver import RESOURCE_ENDPOINTS LOGGER = logging.getLogger(__name__) @@ -28,6 +30,8 @@ def find_key(resource, key): return json.loads(resource[1])[key] # this function exports only the endpoints which are not already involved in a microwave physical link +# TODO: improvement: create a Set[Tuple[node_id:str, tp_id:str]] containing the endpoints involved in links +# TODO: exportable endpoints are those not in this set. That will prevent looping through links for every endpoint def is_exportable_endpoint(node, termination_point_id, links): # for each link we check if the endpoint (termination_point_id) is already used by an existing link for link in links: @@ -39,7 +43,10 @@ def is_exportable_endpoint(node, termination_point_id, links): return False return True -def config_getter(root_url, resource_key, timeout): +def config_getter( + root_url : str, resource_key : str, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None, + node_ids : Set[str] = set() +): # getting endpoints network_name = 'SIAE-ETH-TOPOLOGY' FIELDS = ''.join([ @@ -51,51 +58,53 @@ def config_getter(root_url, resource_key, timeout): url = URL_TEMPLATE.format(root_url, network_name, FIELDS) result = [] - try: - response = requests.get(url, timeout=timeout, verify=False) - except requests.exceptions.Timeout: - LOGGER.exception('Timeout connecting {:s}'.format(url)) - except Exception as e: # pylint: disable=broad-except - LOGGER.exception('Exception retrieving {:s}'.format(resource_key)) - result.append((resource_key, e)) - else: - context = json.loads(response.content) - if resource_key == RESOURCE_ENDPOINTS: + if resource_key == RESOURCE_ENDPOINTS: + # getting existing endpoints + try: + response = requests.get(url, timeout=timeout, verify=False, auth=auth) + context = json.loads(response.content) network_instance = context.get('ietf-network:network', {}) links = network_instance.get('ietf-network-topology:link', []) - for sip in network_instance['node']: - tp = sip['ietf-network-topology:termination-point'] - node_id = sip['node-id'] - for te in tp: - tp_id = te['tp-id'] + for node in network_instance['node']: + node_id = node['node-id'] + if len(node_ids) > 0 and node_id not in node_ids: continue + tp_list = node['ietf-network-topology:termination-point'] + for tp in tp_list: + tp_id = tp['tp-id'] if not is_exportable_endpoint(node_id, tp_id, links): continue - resource_key = '/endpoints/endpoint[{:s}:{:s}]'.format(node_id,tp_id) - resource_value = {'uuid': tp_id, 'type': te['ietf-te-topology:te']['name']} + tp_uuid = '{:s}:{:s}'.format(node_id,tp_id) + resource_key = '/endpoints/endpoint[{:s}]'.format(tp_uuid) + resource_value = {'uuid': tp_uuid, 'type': tp['ietf-te-topology:te']['name']} result.append((resource_key, resource_value)) - - # getting created services - url = '{:s}/nmswebs/restconf/data/ietf-eth-tran-service:etht-svc'.format(root_url) - try: - response = requests.get(url, timeout=timeout, verify=False) - except requests.exceptions.Timeout: - LOGGER.exception('Timeout connecting {:s}'.format(url)) - except Exception as e: # pylint: disable=broad-except - LOGGER.exception('Exception retrieving {:s}'.format(resource_key)) - result.append((resource_key, e)) + except requests.exceptions.Timeout: + LOGGER.exception('Timeout connecting {:s}'.format(url)) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Exception retrieving/parsing endpoints for {:s}'.format(resource_key)) + result.append((resource_key, e)) else: - context = json.loads(response.content) - if resource_key == RESOURCE_ENDPOINTS: + # getting created services + url = '{:s}/nmswebs/restconf/data/ietf-eth-tran-service:etht-svc'.format(root_url) + try: + response = requests.get(url, timeout=timeout, verify=False, auth=auth) + context = json.loads(response.content) etht_service = context.get('ietf-eth-tran-service:etht-svc', {}) service_instances = etht_service.get('etht-svc-instances', []) for service in service_instances: service_name = service['etht-svc-name'] resource_key = '/services/service[{:s}]'.format(service_name) - resource_value = {'uuid': service_name, 'type': service['etht-svc-type']} - result.append((resource_key, resource_value)) + result.append((resource_key, service)) + except requests.exceptions.Timeout: + LOGGER.exception('Timeout connecting {:s}'.format(url)) + except Exception as e: # pylint: disable=broad-except + LOGGER.exception('Exception retrieving/parsing services for {:s}'.format(resource_key)) + result.append((resource_key, e)) + return result def create_connectivity_service( - root_url, timeout, uuid, node_id_src, tp_id_src, node_id_dst, tp_id_dst, vlan_id): + root_url, uuid, node_id_src, tp_id_src, node_id_dst, tp_id_dst, vlan_id, + auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None +): url = '{:s}/nmswebs/restconf/data/ietf-eth-tran-service:etht-svc'.format(root_url) headers = {'content-type': 'application/json'} @@ -128,7 +137,8 @@ def create_connectivity_service( results = [] try: LOGGER.info('Connectivity service {:s}: {:s}'.format(str(uuid), str(data))) - response = requests.post(url=url, data=json.dumps(data), timeout=timeout, headers=headers, verify=False) + response = requests.post( + url=url, data=json.dumps(data), timeout=timeout, headers=headers, verify=False, auth=auth) LOGGER.info('Microwave Driver response: {:s}'.format(str(response))) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Exception creating ConnectivityService(uuid={:s}, data={:s})'.format(str(uuid), str(data))) @@ -140,12 +150,12 @@ def create_connectivity_service( results.append(response.status_code in HTTP_OK_CODES) return results -def delete_connectivity_service(root_url, timeout, uuid): +def delete_connectivity_service(root_url, uuid, auth : Optional[HTTPBasicAuth] = None, timeout : Optional[int] = None): url = '{:s}/nmswebs/restconf/data/ietf-eth-tran-service:etht-svc/etht-svc-instances={:s}' url = url.format(root_url, uuid) results = [] try: - response = requests.delete(url=url, timeout=timeout, verify=False) + response = requests.delete(url=url, timeout=timeout, verify=False, auth=auth) except Exception as e: # pylint: disable=broad-except LOGGER.exception('Exception deleting ConnectivityService(uuid={:s})'.format(str(uuid))) results.append(e) -- GitLab From faa647536105ce9988e54d31843190894f1f593e Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 22 Dec 2022 15:36:35 +0000 Subject: [PATCH 104/353] Service component - MicroWave service handler - corrected service resource keys - corrected endpoint count in set/delete endpoint methods --- .../service_handlers/microwave/MicrowaveServiceHandler.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py b/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py index 5b05160de..fb54a1bc1 100644 --- a/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py +++ b/src/service/service/service_handlers/microwave/MicrowaveServiceHandler.py @@ -81,7 +81,7 @@ class MicrowaveServiceHandler(_ServiceHandler): device_uuid = endpoints[0][0] device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) - json_config_rule = json_config_rule_set('/service[{:s}]'.format(service_uuid), { + json_config_rule = json_config_rule_set('/services/service[{:s}]'.format(service_uuid), { 'uuid' : service_uuid, 'node_id_src': node_id_src, 'tp_id_src' : tp_id_src, @@ -111,11 +111,13 @@ class MicrowaveServiceHandler(_ServiceHandler): results = [] try: chk_type('endpoints', endpoints, list) - if len(endpoints) != 2: raise Exception('len(endpoints) != 2') + if len(endpoints) < 1: raise Exception('len(endpoints) < 1') device_uuid = endpoints[0][0] device = self.__task_executor.get_device(DeviceId(**json_device_id(device_uuid))) - json_config_rule = json_config_rule_delete('/service[{:s}]'.format(service_uuid), {'uuid': service_uuid}) + json_config_rule = json_config_rule_delete('/services/service[{:s}]'.format(service_uuid), { + 'uuid': service_uuid + }) del device.device_config.config_rules[:] device.device_config.config_rules.append(ConfigRule(**json_config_rule)) self.__task_executor.configure_device(device) -- GitLab From 42de2fbe7e2d1b749dc47e4cbb26c01e2ce4e906 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Thu, 22 Dec 2022 15:38:16 +0000 Subject: [PATCH 105/353] Tools - load-gen: - added support for microwave services - removed DLT from default list of components - added support to re-use endpoints in a single device for multiple connections --- src/tests/tools/load_gen/Constants.py | 1 + src/tests/tools/load_gen/RequestGenerator.py | 49 ++++++++++++++------ src/tests/tools/load_gen/__main__.py | 1 + src/tests/tools/load_gen/deploy_specs.sh | 2 +- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/tests/tools/load_gen/Constants.py b/src/tests/tools/load_gen/Constants.py index 1eeb62686..32b457bae 100644 --- a/src/tests/tools/load_gen/Constants.py +++ b/src/tests/tools/load_gen/Constants.py @@ -18,6 +18,7 @@ class RequestType(Enum): SERVICE_L2NM = 'svc-l2nm' SERVICE_L3NM = 'svc-l3nm' SERVICE_TAPI = 'svc-tapi' + SERVICE_MW = 'svc-mw' SLICE_L2NM = 'slc-l2nm' SLICE_L3NM = 'slc-l3nm' diff --git a/src/tests/tools/load_gen/RequestGenerator.py b/src/tests/tools/load_gen/RequestGenerator.py index b0ec9bea6..d38291d38 100644 --- a/src/tests/tools/load_gen/RequestGenerator.py +++ b/src/tests/tools/load_gen/RequestGenerator.py @@ -100,7 +100,8 @@ class RequestGenerator: LOGGER.info('[dump_state] used_device_endpoints = {:s}'.format(json.dumps(self._used_device_endpoints))) def _use_device_endpoint( - self, service_uuid : str, endpoint_types : Optional[Set[str]] = None, exclude_device_uuids : Set[str] = set() + self, service_uuid : str, request_type : RequestType, endpoint_types : Optional[Set[str]] = None, + exclude_device_uuids : Set[str] = set(), exclude_endpoint_uuids : Set[Tuple[str, str]] = set(), ) -> Optional[Tuple[str, str]]: with self._lock: compatible_endpoints : Set[Tuple[str, str]] = set() @@ -109,9 +110,14 @@ class RequestGenerator: if endpoint_types is None: # allow all elegible_device_endpoints : Dict[str, Set[str]] = { - device_uuid:device_endpoint_uuids + device_uuid:[ + endpoint_uuid for endpoint_uuid in device_endpoint_uuids + if (len(exclude_endpoint_uuids) == 0) or \ + ((device_uuid,endpoint_uuid) not in exclude_endpoint_uuids) + ] for device_uuid,device_endpoint_uuids in self._available_device_endpoints.items() - if device_uuid not in exclude_device_uuids and len(device_endpoint_uuids) > 0 + if (device_uuid not in exclude_device_uuids) and \ + (len(device_endpoint_uuids) > 0) } else: # allow only compatible endpoints @@ -123,6 +129,7 @@ class RequestGenerator: if device_uuid in exclude_device_uuids or len(device_endpoint_uuids) == 0: continue for endpoint_uuid in device_endpoint_uuids: endpoint_key = (device_uuid,endpoint_uuid) + if endpoint_key in exclude_endpoint_uuids: continue if endpoint_key not in compatible_endpoints: continue elegible_device_endpoints.setdefault(device_uuid, set()).add(endpoint_uuid) @@ -136,16 +143,19 @@ class RequestGenerator: 'compatible_endpoints={:s}'.format(str(compatible_endpoints)), ])) return None + device_uuid = random.choice(list(elegible_device_endpoints.keys())) device_endpoint_uuids = elegible_device_endpoints.get(device_uuid) endpoint_uuid = random.choice(list(device_endpoint_uuids)) - self._available_device_endpoints.setdefault(device_uuid, set()).discard(endpoint_uuid) - self._used_device_endpoints.setdefault(device_uuid, dict())[endpoint_uuid] = service_uuid + if request_type not in {RequestType.SERVICE_MW}: + # reserve the resources + self._available_device_endpoints.setdefault(device_uuid, set()).discard(endpoint_uuid) + self._used_device_endpoints.setdefault(device_uuid, dict())[endpoint_uuid] = service_uuid return device_uuid, endpoint_uuid def _release_device_endpoint(self, device_uuid : str, endpoint_uuid : str) -> None: with self._lock: - self._used_device_endpoints.setdefault(device_uuid, set()).pop(endpoint_uuid, None) + self._used_device_endpoints.setdefault(device_uuid, dict()).pop(endpoint_uuid, None) self._available_device_endpoints.setdefault(device_uuid, set()).add(endpoint_uuid) def compose_request(self) -> Optional[Dict]: @@ -159,7 +169,9 @@ class RequestGenerator: # choose request type request_type = random.choice(self._parameters.request_types) - if request_type in {RequestType.SERVICE_L2NM, RequestType.SERVICE_L3NM, RequestType.SERVICE_TAPI}: + if request_type in { + RequestType.SERVICE_L2NM, RequestType.SERVICE_L3NM, RequestType.SERVICE_TAPI, RequestType.SERVICE_MW + }: return self._compose_service(num_request, request_uuid, request_type) elif request_type in {RequestType.SLICE_L2NM, RequestType.SLICE_L3NM}: return self._compose_slice(num_request, request_uuid, request_type) @@ -167,7 +179,7 @@ class RequestGenerator: def _compose_service(self, num_request : int, request_uuid : str, request_type : str) -> Optional[Dict]: # choose source endpoint src_endpoint_types = set(ENDPOINT_COMPATIBILITY.keys()) if request_type in {RequestType.SERVICE_TAPI} else None - src = self._use_device_endpoint(request_uuid, endpoint_types=src_endpoint_types) + src = self._use_device_endpoint(request_uuid, request_type, endpoint_types=src_endpoint_types) if src is None: LOGGER.warning('>> No source endpoint is available') return None @@ -179,11 +191,12 @@ class RequestGenerator: dst_endpoint_types = {dst_endpoint_type} if request_type in {RequestType.SERVICE_TAPI} else None # identify excluded destination devices - exclude_device_uuids = {} if request_type in {RequestType.SERVICE_TAPI} else {src_device_uuid} + exclude_device_uuids = {} if request_type in {RequestType.SERVICE_TAPI, RequestType.SERVICE_MW} else {src_device_uuid} # choose feasible destination endpoint dst = self._use_device_endpoint( - request_uuid, endpoint_types=dst_endpoint_types, exclude_device_uuids=exclude_device_uuids) + request_uuid, request_type, endpoint_types=dst_endpoint_types, exclude_device_uuids=exclude_device_uuids, + exclude_endpoint_uuids={src}) # if destination endpoint not found, release source, and terminate current service generation if dst is None: @@ -281,19 +294,29 @@ class RequestGenerator: return json_service_tapi_planned( request_uuid, endpoint_ids=endpoint_ids, constraints=[], config_rules=config_rules) + elif request_type == RequestType.SERVICE_MW: + vlan_id = 1000 + num_request % 1000 + config_rules = [ + json_config_rule_set('/settings', { + 'vlan_id': vlan_id, + }), + ] + return json_service_l2nm_planned( + request_uuid, endpoint_ids=endpoint_ids, constraints=[], config_rules=config_rules) + def _compose_slice(self, num_request : int, request_uuid : str, request_type : str) -> Optional[Dict]: # choose source endpoint - src = self._use_device_endpoint(request_uuid) + src = self._use_device_endpoint(request_uuid, request_type) if src is None: LOGGER.warning('>> No source endpoint is available') return None src_device_uuid,src_endpoint_uuid = src # identify excluded destination devices - exclude_device_uuids = {} if request_type in {RequestType.SERVICE_TAPI} else {src_device_uuid} + exclude_device_uuids = {} if request_type in {RequestType.SERVICE_TAPI, RequestType.SERVICE_MW} else {src_device_uuid} # choose feasible destination endpoint - dst = self._use_device_endpoint(request_uuid, exclude_device_uuids=exclude_device_uuids) + dst = self._use_device_endpoint(request_uuid, request_type, exclude_device_uuids=exclude_device_uuids) # if destination endpoint not found, release source, and terminate current service generation if dst is None: diff --git a/src/tests/tools/load_gen/__main__.py b/src/tests/tools/load_gen/__main__.py index f622f4d14..9a5ea2b69 100644 --- a/src/tests/tools/load_gen/__main__.py +++ b/src/tests/tools/load_gen/__main__.py @@ -28,6 +28,7 @@ def main(): request_types = [ RequestType.SERVICE_L2NM, RequestType.SERVICE_L3NM, + #RequestType.SERVICE_MW, #RequestType.SERVICE_TAPI, RequestType.SLICE_L2NM, RequestType.SLICE_L3NM, diff --git a/src/tests/tools/load_gen/deploy_specs.sh b/src/tests/tools/load_gen/deploy_specs.sh index 1982ef227..a688f1c0a 100644 --- a/src/tests/tools/load_gen/deploy_specs.sh +++ b/src/tests/tools/load_gen/deploy_specs.sh @@ -7,7 +7,7 @@ export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" # interdomain slice pathcomp dlt # dbscanserving opticalattackmitigator opticalattackdetector # l3_attackmitigator l3_centralizedattackdetector l3_distributedattackdetector -export TFS_COMPONENTS="context device pathcomp service slice webui dlt" # automation monitoring compute +export TFS_COMPONENTS="context device pathcomp service slice webui" # automation monitoring compute dlt # Set the tag you want to use for your images. export TFS_IMAGE_TAG="dev" -- GitLab From a8e2c9b3b1ada55a6aded67b951cc18e1c84578c Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 23 Dec 2022 12:07:49 +0000 Subject: [PATCH 106/353] Context component: - updated manifest - corrected README.md notes - corrected script run-tests-locally - partial code implementation --- manifests/cockroachdb/README.md | 18 ++--- manifests/contextservice.yaml | 2 +- scripts/run_tests_locally-context.sh | 4 +- .../service/ContextServiceServicerImpl.py | 71 ++++++++++++------- src/context/tests/test_unitary.py | 28 ++++---- 5 files changed, 69 insertions(+), 54 deletions(-) diff --git a/manifests/cockroachdb/README.md b/manifests/cockroachdb/README.md index 6807afbb0..ce99f5034 100644 --- a/manifests/cockroachdb/README.md +++ b/manifests/cockroachdb/README.md @@ -12,7 +12,7 @@ kubectl apply -f "${DEPLOY_PATH}/crds.yaml" # Deploy CockroachDB Operator curl -o "${DEPLOY_PATH}/operator.yaml" "${OPERATOR_BASE_URL}/install/operator.yaml" # edit "${DEPLOY_PATH}/operator.yaml" -# - add env var: WATCH_NAMESPACE='tfs-ccdb' +# - add env var: WATCH_NAMESPACE='tfs-crdb' kubectl apply -f "${DEPLOY_PATH}/operator.yaml" # Deploy CockroachDB @@ -20,21 +20,21 @@ curl -o "${DEPLOY_PATH}/cluster.yaml" "${OPERATOR_BASE_URL}/examples/example.yam # edit "${DEPLOY_PATH}/cluster.yaml" # - set version # - set number of replicas -kubectl create namespace tfs-ccdb -kubectl apply --namespace tfs-ccdb -f "${DEPLOY_PATH}/cluster.yaml" +kubectl create namespace tfs-crdb +kubectl apply --namespace tfs-crdb -f "${DEPLOY_PATH}/cluster.yaml" # Deploy CockroachDB Client curl -o "${DEPLOY_PATH}/client-secure-operator.yaml" "${OPERATOR_BASE_URL}/examples/client-secure-operator.yaml" -kubectl create --namespace tfs-ccdb -f "${DEPLOY_PATH}/client-secure-operator.yaml" +kubectl create --namespace tfs-crdb -f "${DEPLOY_PATH}/client-secure-operator.yaml" # Add tfs user with admin rights -$ kubectl exec -it ccdb-client-secure --namespace tfs-ccdb -- ./cockroach sql --certs-dir=/cockroach/cockroach-certs --host=cockroachdb-public +$ kubectl exec -it cockroachdb-client-secure --namespace tfs-crdb -- ./cockroach sql --certs-dir=/cockroach/cockroach-certs --host=cockroachdb-public -- CREATE USER tfs WITH PASSWORD 'tfs123'; -- GRANT admin TO tfs; # Expose CockroachDB SQL port (26257) -PORT=$(kubectl --namespace cockroachdb get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}') -PATCH='{"data": {"'${PORT}'": "cockroachdb/cockroachdb-public:'${PORT}'"}}' +PORT=$(kubectl --namespace tfs-crdb get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="sql")].port}') +PATCH='{"data": {"'${PORT}'": "tfs-crdb/cockroachdb-public:'${PORT}'"}}' kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" PORT_MAP='{"containerPort": '${PORT}', "hostPort": '${PORT}'}' @@ -43,8 +43,8 @@ PATCH='{"spec": {"template": {"spec": {"containers": ['${CONTAINER}']}}}}' kubectl patch daemonset nginx-ingress-microk8s-controller --namespace ingress --patch "${PATCH}" # Expose CockroachDB Console port (8080) -PORT=$(kubectl --namespace cockroachdb get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="http")].port}') -PATCH='{"data": {"'${PORT}'": "cockroachdb/cockroachdb-public:'${PORT}'"}}' +PORT=$(kubectl --namespace tfs-crdb get service cockroachdb-public -o 'jsonpath={.spec.ports[?(@.name=="http")].port}') +PATCH='{"data": {"'${PORT}'": "tfs-crdb/cockroachdb-public:'${PORT}'"}}' kubectl patch configmap nginx-ingress-tcp-microk8s-conf --namespace ingress --patch "${PATCH}" PORT_MAP='{"containerPort": '${PORT}', "hostPort": '${PORT}'}' diff --git a/manifests/contextservice.yaml b/manifests/contextservice.yaml index 8201aed3e..dc7e548ce 100644 --- a/manifests/contextservice.yaml +++ b/manifests/contextservice.yaml @@ -47,7 +47,7 @@ spec: - containerPort: 8080 env: - name: CCDB_URL - value: "cockroachdb://tfs:tfs123@cockroachdb-public.cockroachdb.svc.cluster.local:26257/tfs?sslmode=require" + value: "cockroachdb://tfs:tfs123@10.1.7.195:26257/tfs?sslmode=require" - name: DB_BACKEND value: "redis" - name: MB_BACKEND diff --git a/scripts/run_tests_locally-context.sh b/scripts/run_tests_locally-context.sh index bf0cccd6b..ec12d8a80 100755 --- a/scripts/run_tests_locally-context.sh +++ b/scripts/run_tests_locally-context.sh @@ -36,13 +36,13 @@ cd $PROJECTDIR/src #export REDIS_SERVICE_HOST=$(kubectl get node $TFS_K8S_HOSTNAME -o 'jsonpath={.status.addresses[?(@.type=="InternalIP")].address}') #export REDIS_SERVICE_PORT=$(kubectl --namespace $TFS_K8S_NAMESPACE get service redis-tests -o 'jsonpath={.spec.ports[?(@.port==6379)].nodePort}') -export CRDB_URI="cockroachdb://tfs:tfs123@127.0.0.1:26257/tfs?sslmode=require" +export CRDB_URI="cockroachdb://tfs:tfs123@10.1.7.195:26257/tfs?sslmode=require" +export PYTHONPATH=/home/tfs/tfs-ctrl/src # Run unitary tests and analyze coverage of code at same time #coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose --maxfail=1 \ # context/tests/test_unitary.py -source tfs_runtime_env_vars.sh pytest --log-level=INFO --verbose -o log_cli=true --maxfail=1 \ context/tests/test_unitary.py diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py index fcb0024d2..f51e725cd 100644 --- a/src/context/service/ContextServiceServicerImpl.py +++ b/src/context/service/ContextServiceServicerImpl.py @@ -62,7 +62,7 @@ from context.service.database.ContextModel import ContextModel #from .Constants import ( # CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE, # TOPIC_TOPOLOGY) -from .ChangeFeedClient import ChangeFeedClient +#from .ChangeFeedClient import ChangeFeedClient LOGGER = logging.getLogger(__name__) @@ -178,37 +178,54 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer @safe_and_metered_rpc_method(METRICS, LOGGER) def GetContextEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[ContextEvent]: + pass #for message in self.messagebroker.consume({TOPIC_CONTEXT}, consume_timeout=CONSUME_TIMEOUT): # yield ContextEvent(**json.loads(message.content)) - cf = ChangeFeedClient() - ready = cf.initialize() - if not ready: raise OperationFailedException('Initialize ChangeFeed') - for timestamp, _, primary_key, is_delete, after in cf.get_changes('context'): - if is_delete: - event_type = EventTypeEnum.EVENTTYPE_REMOVE - else: - is_create = (timestamp - after.get('created_at')) < 1.0 - event_type = EventTypeEnum.EVENTTYPE_CREATE if is_create else EventTypeEnum.EVENTTYPE_UPDATE - event = { - 'event': {'timestamp': {'timestamp': timestamp}, 'event_type': event_type}, - 'context_id': json_context_id(primary_key[0]), - } - yield ContextEvent(**event) + #cf = ChangeFeedClient() + #ready = cf.initialize() + #if not ready: raise OperationFailedException('Initialize ChangeFeed') + #for timestamp, _, primary_key, is_delete, after in cf.get_changes('context'): + # if is_delete: + # event_type = EventTypeEnum.EVENTTYPE_REMOVE + # else: + # is_create = (timestamp - after.get('created_at')) < 1.0 + # event_type = EventTypeEnum.EVENTTYPE_CREATE if is_create else EventTypeEnum.EVENTTYPE_UPDATE + # event = { + # 'event': {'timestamp': {'timestamp': timestamp}, 'event_type': event_type}, + # 'context_id': json_context_id(primary_key[0]), + # } + # yield ContextEvent(**event) # ----- Topology --------------------------------------------------------------------------------------------------- -# @safe_and_metered_rpc_method(METRICS, LOGGER) -# def ListTopologyIds(self, request: ContextId, context : grpc.ServicerContext) -> TopologyIdList: -# context_uuid = request.context_uuid.uuid -# -# with self.session() as session: -# result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by(context_uuid=context_uuid).one_or_none() -# if not result: -# raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) -# -# db_topologies = result.topology -# return TopologyIdList(topology_ids=[db_topology.dump_id() for db_topology in db_topologies]) -# + @safe_and_metered_rpc_method(METRICS, LOGGER) + def ListTopologyIds(self, request: ContextId, context : grpc.ServicerContext) -> TopologyIdList: + context_uuid = request.context_uuid.uuid + + with self.session() as session: + result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by(context_uuid=context_uuid).one_or_none() + if not result: + raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) + + db_topologies = result.topology + return TopologyIdList(topology_ids=[db_topology.dump_id() for db_topology in db_topologies]) + return ContextIdList(context_ids=run_transaction(sessionmaker(bind=self.db_engine), callback)) + + + def callback(session : Session) -> List[Dict]: + obj_list : List[ContextModel] = session.query(ContextModel).all() + return [obj.dump_id() for obj in obj_list] + + + @safe_and_metered_rpc_method(METRICS, LOGGER) + def ListContexts(self, request: Empty, context : grpc.ServicerContext) -> ContextList: + def callback(session : Session) -> List[Dict]: + obj_list : List[ContextModel] = session.query(ContextModel).all() + return [obj.dump() for obj in obj_list] + return ContextList(contexts=run_transaction(sessionmaker(bind=self.db_engine), callback)) + + + # @safe_and_metered_rpc_method(METRICS, LOGGER) # def ListTopologies(self, request: ContextId, context : grpc.ServicerContext) -> TopologyList: # context_uuid = request.context_uuid.uuid diff --git a/src/context/tests/test_unitary.py b/src/context/tests/test_unitary.py index 8bf1b4ff1..32c571359 100644 --- a/src/context/tests/test_unitary.py +++ b/src/context/tests/test_unitary.py @@ -145,12 +145,12 @@ def test_grpc_context( rebuild_database(db_engine, drop_if_exists=True) # ----- Initialize the EventsCollector ----------------------------------------------------------------------------- - events_collector = EventsCollector( - context_client_grpc, log_events_received=True, - activate_context_collector = True, activate_topology_collector = False, activate_device_collector = False, - activate_link_collector = False, activate_service_collector = False, activate_slice_collector = False, - activate_connection_collector = False) - events_collector.start() + #events_collector = EventsCollector( + # context_client_grpc, log_events_received=True, + # activate_context_collector = True, activate_topology_collector = False, activate_device_collector = False, + # activate_link_collector = False, activate_service_collector = False, activate_slice_collector = False, + # activate_connection_collector = False) + #events_collector.start() # ----- Get when the object does not exist ------------------------------------------------------------------------- with pytest.raises(grpc.RpcError) as e: @@ -207,8 +207,8 @@ def test_grpc_context( assert e.value.details() == msg # ----- Check create event ----------------------------------------------------------------------------------------- - event = events_collector.get_event(block=True, timeout=10.0) - assert isinstance(event, ContextEvent) + #event = events_collector.get_event(block=True, timeout=10.0) + #assert isinstance(event, ContextEvent) #assert event.event.event_type == EventTypeEnum.EVENTTYPE_CREATE #assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID @@ -241,8 +241,8 @@ def test_grpc_context( assert response.context_uuid.uuid == DEFAULT_CONTEXT_UUID # ----- Check update event ----------------------------------------------------------------------------------------- - event = events_collector.get_event(block=True, timeout=10.0) - assert isinstance(event, ContextEvent) + #event = events_collector.get_event(block=True, timeout=10.0) + #assert isinstance(event, ContextEvent) #assert event.event.event_type == EventTypeEnum.EVENTTYPE_UPDATE #assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID @@ -279,8 +279,8 @@ def test_grpc_context( context_client_grpc.RemoveContext(ContextId(**CONTEXT_ID)) # ----- Check remove event ----------------------------------------------------------------------------------------- - event = events_collector.get_event(block=True, timeout=10.0) - assert isinstance(event, ContextEvent) + #event = events_collector.get_event(block=True, timeout=10.0) + #assert isinstance(event, ContextEvent) #assert event.event.event_type == EventTypeEnum.EVENTTYPE_REMOVE #assert event.context_id.context_uuid.uuid == DEFAULT_CONTEXT_UUID @@ -292,7 +292,7 @@ def test_grpc_context( assert len(response.contexts) == 0 # ----- Stop the EventsCollector ----------------------------------------------------------------------------------- - events_collector.stop() + #events_collector.stop() # ----- Dump state of database after remove the object ------------------------------------------------------------- #db_entries = database.dump_all() @@ -302,8 +302,6 @@ def test_grpc_context( #LOGGER.info('-----------------------------------------------------------') #assert len(db_entries) == 0 - raise Exception() - """ def test_grpc_topology( context_client_grpc: ContextClient, # pylint: disable=redefined-outer-name -- GitLab From 77483ce3b5410bc02b2dbca883cf0bea31dabef1 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Fri, 23 Dec 2022 15:26:12 +0000 Subject: [PATCH 107/353] Context component: - partial code implementation --- .../service/ContextServiceServicerImpl.py | 93 ++++++++++--------- src/context/service/database/ContextModel.py | 11 ++- src/context/service/database/TopologyModel.py | 35 +++---- src/context/service/database/__init__.py | 1 + 4 files changed, 75 insertions(+), 65 deletions(-) diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py index f51e725cd..6db5b99e7 100644 --- a/src/context/service/ContextServiceServicerImpl.py +++ b/src/context/service/ContextServiceServicerImpl.py @@ -58,7 +58,7 @@ from context.service.database.ContextModel import ContextModel #from context.service.database.ServiceModel import ( # ServiceModel, grpc_to_enum__service_status, grpc_to_enum__service_type) #from context.service.database.SliceModel import SliceModel, grpc_to_enum__slice_status -#from context.service.database.TopologyModel import TopologyModel +from context.service.database.TopologyModel import TopologyModel #from .Constants import ( # CONSUME_TIMEOUT, TOPIC_CONNECTION, TOPIC_CONTEXT, TOPIC_DEVICE, TOPIC_LINK, TOPIC_SERVICE, TOPIC_SLICE, # TOPIC_TOPOLOGY) @@ -111,8 +111,10 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer def GetContext(self, request: ContextId, context : grpc.ServicerContext) -> Context: context_uuid = request.context_uuid.uuid def callback(session : Session) -> Optional[Dict]: - obj : Optional[ContextModel] = \ - session.query(ContextModel).filter_by(context_uuid=context_uuid).one_or_none() + obj : Optional[ContextModel] = session\ + .query(ContextModel)\ + .filter_by(context_uuid=context_uuid)\ + .one_or_none() return None if obj is None else obj.dump() obj = run_transaction(sessionmaker(bind=self.db_engine), callback) if obj is None: raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) @@ -202,47 +204,50 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer def ListTopologyIds(self, request: ContextId, context : grpc.ServicerContext) -> TopologyIdList: context_uuid = request.context_uuid.uuid - with self.session() as session: - result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by(context_uuid=context_uuid).one_or_none() - if not result: - raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) - - db_topologies = result.topology - return TopologyIdList(topology_ids=[db_topology.dump_id() for db_topology in db_topologies]) - return ContextIdList(context_ids=run_transaction(sessionmaker(bind=self.db_engine), callback)) - - def callback(session : Session) -> List[Dict]: - obj_list : List[ContextModel] = session.query(ContextModel).all() + obj_list : List[TopologyModel] = session.query(TopologyModel).filter_by(context_uuid=context_uuid).all() + #.options(selectinload(ContextModel.topology)).filter_by(context_uuid=context_uuid).one_or_none() return [obj.dump_id() for obj in obj_list] - + + #with self.session() as session: + # result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by(context_uuid=context_uuid).one_or_none() + # if not result: + # raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) + # db_topologies = result.topology + return TopologyIdList(topology_ids=run_transaction(sessionmaker(bind=self.db_engine), callback)) @safe_and_metered_rpc_method(METRICS, LOGGER) - def ListContexts(self, request: Empty, context : grpc.ServicerContext) -> ContextList: + def ListTopologies(self, request: ContextId, context : grpc.ServicerContext) -> TopologyList: + context_uuid = request.context_uuid.uuid + def callback(session : Session) -> List[Dict]: - obj_list : List[ContextModel] = session.query(ContextModel).all() + obj_list : List[TopologyModel] = session.query(TopologyModel).filter_by(context_uuid=context_uuid).all() + #.options(selectinload(ContextModel.topology)).filter_by(context_uuid=context_uuid).one_or_none() return [obj.dump() for obj in obj_list] - return ContextList(contexts=run_transaction(sessionmaker(bind=self.db_engine), callback)) + #with self.session() as session: + # result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by( + # context_uuid=context_uuid).one_or_none() + # if not result: + # raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) + # db_topologies = result.topology + return TopologyList(topologies=run_transaction(sessionmaker(bind=self.db_engine), callback)) + @safe_and_metered_rpc_method(METRICS, LOGGER) + def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology: + context_uuid = request.context_id.context_uuid.uuid + topology_uuid = request.topology_uuid.uuid + + def callback(session : Session) -> Optional[Dict]: + obj : Optional[TopologyModel] = session\ + .query(TopologyModel)\ + .filter_by(context_uuid=context_uuid, topology_uuid=topology_uuid)\ + .one_or_none() + return None if obj is None else obj.dump() + obj = run_transaction(sessionmaker(bind=self.db_engine), callback) + if obj is None: raise NotFoundException(TopologyModel.__name__.replace('Model', ''), context_uuid) + return Topology(**obj) -# @safe_and_metered_rpc_method(METRICS, LOGGER) -# def ListTopologies(self, request: ContextId, context : grpc.ServicerContext) -> TopologyList: -# context_uuid = request.context_uuid.uuid -# -# with self.session() as session: -# result = session.query(ContextModel).options(selectinload(ContextModel.topology)).filter_by( -# context_uuid=context_uuid).one_or_none() -# if not result: -# raise NotFoundException(ContextModel.__name__.replace('Model', ''), context_uuid) -# -# db_topologies = result.topology -# return TopologyList(topologies=[db_topology.dump() for db_topology in db_topologies]) -# -# @safe_and_metered_rpc_method(METRICS, LOGGER) -# def GetTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Topology: -# topology_uuid = request.topology_uuid.uuid -# # result, dump = self.database.get_object(TopologyModel, topology_uuid, True) # with self.session() as session: # devs = None @@ -265,8 +270,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer # links.append(session.query(LinkModel).filter_by(**filt).one()) # # return Topology(**result.dump(devs, links)) -# -# + # @safe_and_metered_rpc_method(METRICS, LOGGER) # def SetTopology(self, request: Topology, context : grpc.ServicerContext) -> TopologyId: # context_uuid = request.topology_id.context_id.context_uuid.uuid @@ -300,7 +304,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer # dict_topology_id = db_topology.dump_id() # notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) # return TopologyId(**dict_topology_id) -# + # @safe_and_metered_rpc_method(METRICS, LOGGER) # def RemoveTopology(self, request: TopologyId, context : grpc.ServicerContext) -> Empty: # context_uuid = request.context_id.context_uuid.uuid @@ -317,13 +321,12 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer # event_type = EventTypeEnum.EVENTTYPE_REMOVE # notify_event(self.messagebroker, TOPIC_TOPOLOGY, event_type, {'topology_id': dict_topology_id}) # return Empty() -# -## @safe_and_metered_rpc_method(METRICS, LOGGER) -## def GetTopologyEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[TopologyEvent]: -## for message in self.messagebroker.consume({TOPIC_TOPOLOGY}, consume_timeout=CONSUME_TIMEOUT): -## yield TopologyEvent(**json.loads(message.content)) -# -# + +# @safe_and_metered_rpc_method(METRICS, LOGGER) +# def GetTopologyEvents(self, request: Empty, context : grpc.ServicerContext) -> Iterator[TopologyEvent]: +# for message in self.messagebroker.consume({TOPIC_TOPOLOGY}, consume_timeout=CONSUME_TIMEOUT): +# yield TopologyEvent(**json.loads(message.content)) + # # ----- Device ----------------------------------------------------------------------------------------------------- # # @safe_and_metered_rpc_method(METRICS, LOGGER) diff --git a/src/context/service/database/ContextModel.py b/src/context/service/database/ContextModel.py index 9ad5e0bcb..241198d3f 100644 --- a/src/context/service/database/ContextModel.py +++ b/src/context/service/database/ContextModel.py @@ -16,8 +16,8 @@ import logging from typing import Dict from sqlalchemy import Column, Float, String from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import relationship from ._Base import _Base -#from sqlalchemy.orm import relationship LOGGER = logging.getLogger(__name__) @@ -27,7 +27,7 @@ class ContextModel(_Base): context_name = Column(String(), nullable=False) created_at = Column(Float) - #topology = relationship('TopologyModel', back_populates='context') + topology = relationship('TopologyModel', back_populates='context') def dump_id(self) -> Dict: return {'context_uuid': {'uuid': self.context_uuid}} @@ -48,8 +48,13 @@ class ContextModel(_Base): return [TopologyModel(self.database, pk).dump_id() for pk,_ in db_topology_pks] """ - def dump(self, include_services=True, include_topologies=True) -> Dict: # pylint: disable=arguments-differ + def dump(self, + include_services : bool = True, # pylint: disable=arguments-differ + include_slices : bool = True, # pylint: disable=arguments-differ + include_topologies : bool = True # pylint: disable=arguments-differ + ) -> Dict: result = {'context_id': self.dump_id(), 'name': self.context_name} # if include_services: result['service_ids'] = self.dump_service_ids() + # if include_slices: result['slice_ids'] = self.dump_slice_ids() # if include_topologies: result['topology_ids'] = self.dump_topology_ids() return result diff --git a/src/context/service/database/TopologyModel.py b/src/context/service/database/TopologyModel.py index 0a5698163..102e3ae3f 100644 --- a/src/context/service/database/TopologyModel.py +++ b/src/context/service/database/TopologyModel.py @@ -12,21 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. -import logging, operator -from typing import Dict, List -from sqlalchemy.orm import relationship +import logging #, operator +from typing import Dict #, List from sqlalchemy import Column, ForeignKey from sqlalchemy.dialects.postgresql import UUID -from context.service.database._Base import Base +from sqlalchemy.orm import relationship +from ._Base import _Base + LOGGER = logging.getLogger(__name__) -class TopologyModel(Base): +class TopologyModel(_Base): __tablename__ = 'Topology' - context_uuid = Column(UUID(as_uuid=False), ForeignKey("Context.context_uuid"), primary_key=True) + context_uuid = Column(UUID(as_uuid=False), ForeignKey('context.context_uuid'), primary_key=True) topology_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True) # Relationships - context = relationship("ContextModel", back_populates="topology") + context = relationship('ContextModel', back_populates='topology') def dump_id(self) -> Dict: context_id = self.context.dump_id() @@ -35,16 +36,16 @@ class TopologyModel(Base): 'topology_uuid': {'uuid': self.topology_uuid}, } - @staticmethod - def main_pk_name() -> str: - return 'topology_uuid' + #@staticmethod + #def main_pk_name() -> str: + # return 'topology_uuid' - def dump( # pylint: disable=arguments-differ - self, devices=None, links=None - ) -> Dict: + def dump(self) -> Dict: + # pylint: disable=arguments-differ result = {'topology_id': self.dump_id()} - if devices: - result['device_ids'] = [device.dump_id() for device in devices] - if links: - result['link_ids'] = [link.dump_id() for link in links] + # params: , devices=None, links=None + #if devices: + # result['device_ids'] = [device.dump_id() for device in devices] + #if links: + # result['link_ids'] = [link.dump_id() for link in links] return result diff --git a/src/context/service/database/__init__.py b/src/context/service/database/__init__.py index 980265786..c4940470a 100644 --- a/src/context/service/database/__init__.py +++ b/src/context/service/database/__init__.py @@ -14,3 +14,4 @@ from ._Base import _Base, rebuild_database from .ContextModel import ContextModel +from .TopologyModel import TopologyModel -- GitLab From 44eec9ba7f6daaaeaa18046fc5360846fddc8fb9 Mon Sep 17 00:00:00 2001 From: Ville Hallivuori Date: Fri, 30 Dec 2022 15:20:48 +0200 Subject: [PATCH 108/353] XR Driver Create consistency enforcement --- src/device/service/drivers/xr/XrDriver.py | 21 +++-- src/device/service/drivers/xr/cm-cli.py | 21 ++++- .../service/drivers/xr/cm/cm_connection.py | 80 ++++++++++++++++++- .../service/drivers/xr/cm/connection.py | 44 +++++++++- .../drivers/xr/cm/tests/test_connection.py | 4 + src/tests/ofc22/descriptors_emulated_xr.json | 2 +- 6 files changed, 158 insertions(+), 14 deletions(-) diff --git a/src/device/service/drivers/xr/XrDriver.py b/src/device/service/drivers/xr/XrDriver.py index 51fd29ad1..5fb1a320c 100644 --- a/src/device/service/drivers/xr/XrDriver.py +++ b/src/device/service/drivers/xr/XrDriver.py @@ -20,7 +20,7 @@ from typing import Any, Iterator, List, Optional, Tuple, Union import urllib3 from common.type_checkers.Checkers import chk_type from device.service.driver_api._Driver import _Driver -from .cm.cm_connection import CmConnection +from .cm.cm_connection import CmConnection, ConsistencyMode from .cm import tf # Don't complain about non-verified SSL certificate. This driver is demo only @@ -40,13 +40,22 @@ class XrDriver(_Driver): self.__hub_module_name = settings["hub_module_name"] tls_verify = False # Currently using self signed certificates - username = settings["username"] if "username" in settings else "xr-user-1" - password = settings["password"] if "password" in settings else "xr-user-1" - - self.__cm_connection = CmConnection(address, int(port), username, password, self.__timeout, tls_verify = tls_verify) + username = settings.get("username", "xr-user-1") + password = settings.get("password", "xr-user-1") + + # Options are: + # asynchronous --> operation considered complete when IPM responds with suitable status code, + # including "accepted", that only means request is semantically good and queued. + # synchronous --> operation is considered complete once result is also reflected in GETs in REST API. + # lifecycle --> operation is considered successfull once IPM has completed pluggaable configuration + # or failed in it. This is typically unsuitable for production use + # (as some optics may be transiently unreachable), but is convenient for demos and testin. + consistency_mode = ConsistencyMode.from_str(settings.get("consistency-mode", "asynchronous")) + + self.__cm_connection = CmConnection(address, int(port), username, password, self.__timeout, tls_verify = tls_verify, consistency_mode=consistency_mode) self.__constellation = None - LOGGER.info(f"XrDriver instantiated, cm {address}:{port}, {settings=}") + LOGGER.info(f"XrDriver instantiated, cm {address}:{port}, consistency mode {str(consistency_mode)}, {settings=}") def __str__(self): return f"{self.__hub_module_name}@{self.__cm_address}" diff --git a/src/device/service/drivers/xr/cm-cli.py b/src/device/service/drivers/xr/cm-cli.py index 8b8fec59c..f86ab0c8d 100755 --- a/src/device/service/drivers/xr/cm-cli.py +++ b/src/device/service/drivers/xr/cm-cli.py @@ -19,7 +19,7 @@ import argparse import logging import traceback from typing import Tuple -from cm.cm_connection import CmConnection +from cm.cm_connection import CmConnection, ConsistencyMode from cm.tf_service import TFService from cm.transport_capacity import TransportCapacity from cm.connection import Connection @@ -43,6 +43,8 @@ parser.add_argument('--delete-connection', nargs='?', type=str, help="connection parser.add_argument('--list-transport-capacities', action='store_true') parser.add_argument('--create-transport-capacity', nargs='?', type=str, help="uuid;ifname;ifname;capacity") parser.add_argument('--emulate-tf-set-config-service', nargs='?', type=str, help="hubmodule;uuid;ifname;ifname;capacity or hubmodule;uuid;ifname;ifname;capacity;FORCE-VTI-ON") +parser.add_argument('--consistency-mode', nargs='?', type=str, help="asynchronous|synchronous|lifecycle;RETRY_INTERVAL_FLOAT_AS_S") +parser.add_argument('--timeout', help='REST call timeout in seconds (per request and total for consistency validation)', type=int, default=60) args = parser.parse_args() @@ -66,7 +68,22 @@ def cli_modify_string_to_tf_service(cli_create_str: str) -> Tuple[str, TFService print("Invalid object create arguments. Expecting \"href;oid;ifname1;ifname2;bandwidthgbits\" or \"href;oid;ifname1;ifname2\", where ifname is form \"MODULE|PORT\"") exit(-1) -cm = CmConnection(args.ip, args.port, args.username, args.password, tls_verify=False) +if args.consistency_mode: + ca = args.consistency_mode.split(";") + if 2 != len(ca): + print("Invalid consistency mode specification. Expecting \"asynchronous|synchronous|lifecycle;RETRY_INTERVAL_FLOAT_AS_S\"") + exit(-1) + consistency_mode = ConsistencyMode.from_str(ca[0]) + try: + retry_interval = float(ca[1]) + except ValueError: + print("Invalid consistency mode retry interval (non-float)") + exit(-1) +else: + consistency_mode = ConsistencyMode.lifecycle + retry_interval = 0.2 + +cm = CmConnection(args.ip, args.port, args.username, args.password, timeout=args.timeout, tls_verify=False, consistency_mode=consistency_mode, retry_interval=retry_interval) if not cm.Connect(): exit(-1) diff --git a/src/device/service/drivers/xr/cm/cm_connection.py b/src/device/service/drivers/xr/cm/cm_connection.py index b4aee5866..fc2bb9fb8 100644 --- a/src/device/service/drivers/xr/cm/cm_connection.py +++ b/src/device/service/drivers/xr/cm/cm_connection.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations import collections.abc import logging import json @@ -21,6 +22,7 @@ from typing import Optional, List, Dict, Union import re import requests import urllib3 +from enum import Enum from .connection import Connection from .transport_capacity import TransportCapacity from .constellation import Constellation @@ -49,6 +51,22 @@ class ExpiringValue: class UnexpectedEmptyBody(Exception): pass +# This is enum, not a regular class, see https://docs.python.org/3/library/enum.html +# String based enums require python 3.11, so use nunber based and custom parser +class ConsistencyMode(Enum): + asynchronous = 0 + synchronous = 1 + lifecycle = 2 + + @staticmethod + def from_str(s: str) -> ConsistencyMode: + if "synchronous" == s: + return ConsistencyMode.synchronous + elif "lifecycle" == s: + return ConsistencyMode.lifecycle + # Async is the default + return ConsistencyMode.asynchronous + class HttpResult: def __init__(self, method: str, url: str, params: Dict[str, any] = None): self.method = method @@ -71,7 +89,7 @@ class HttpResult: return f"{self.method} {self.url} {self.params}, status {status_code}, body {body_text}" def process_http_response(self, response: requests.Response, permit_empty_body:bool = False): - LOGGER.info(f"process_http_response(): {self.method}: {self.url} qparams={self.params} ==> {response.status_code}") # FIXME: params + LOGGER.info(f"process_http_response(): {self.method}: {self.url} qparams={self.params} ==> {response.status_code}") self.status_code = response.status_code if response.content != b'null' and len(response.text): self.text = response.text @@ -117,12 +135,16 @@ class HttpResult: return True class CmConnection: - def __init__(self, address: str, port: int, username: str, password: str, timeout=30, tls_verify=True) -> None: + CONSISTENCY_WAIT_LOG_INTERVAL = 1.0 + + def __init__(self, address: str, port: int, username: str, password: str, timeout=30, tls_verify=True, consistency_mode: ConsistencyMode = ConsistencyMode.asynchronous, retry_interval: float=0.2) -> None: self.__tls_verify = tls_verify if not tls_verify: urllib3.disable_warnings() + self.__consistency_mode = consistency_mode self.__timeout = timeout + self.__retry_interval = retry_interval if retry_interval > 0.01 else 0.01 self.__username = username self.__password = password self.__cm_root = 'https://' + address + ':' + str(port) @@ -275,6 +297,50 @@ class CmConnection: LOGGER.info(f"Deleting transport-capacity {href=} failed, status {resp.status_code}") return False + def apply_create_consistency(self, obj, get_fn): + # Asynchronous, no validation + if self.__consistency_mode == ConsistencyMode.asynchronous: + return obj + + ts_start = time.perf_counter() + log_ts = ts_start + get_result = get_fn() + valid = False + while True: + if get_result: + if self.__consistency_mode == ConsistencyMode.synchronous: + valid = True + break + if get_result.life_cycle_info.is_terminal_state(): + valid = True + break + else: + ts = time.perf_counter() + if ts - log_ts >= self.CONSISTENCY_WAIT_LOG_INTERVAL: + log_ts = ts + LOGGER.info(f"apply_create_consistency(): waiting for life cycle state progress for {get_result}, current: {str(get_result.life_cycle_info)}, ellapsed time {ts-ts_start} seconds") + else: + ts = time.perf_counter() + if ts - log_ts >= self.CONSISTENCY_WAIT_LOG_INTERVAL: + log_ts = ts + LOGGER.info(f"apply_create_consistency(): waiting for REST API object for {obj}, ellapsed time {ts-ts_start} seconds") + + if time.perf_counter() - ts_start > self.__timeout: + break + time.sleep(self.__retry_interval) + get_result = get_fn() + + duration = time.perf_counter() - ts_start + if not valid: + if get_result: + LOGGER.info(f"Failed to apply create consistency for {get_result}, insufficient life-cycle-state progress ({str(get_result.life_cycle_info)}), duration {duration} seconds") + else: + LOGGER.info(f"Failed to apply create consistency for {obj}, REST object did not appear, duration {duration} seconds") + else: + LOGGER.info(f"Applied create consistency for {get_result}, final life-cycle-state {str(get_result.life_cycle_info)}, duration {duration} seconds") + + return get_result + def create_connection(self, connection: Connection) -> Optional[str]: # Create wants a list, so wrap connection to list cfg = [connection.create_config()] @@ -282,8 +348,14 @@ class CmConnection: resp = self.__post("/api/v1/ncs/network-connections", cfg) if resp.is_valid_json_list_with_status(202, 1, 1) and "href" in resp.json[0]: connection.href = resp.json[0]["href"] - LOGGER.info(f"Created connection {connection}") - return connection.href + LOGGER.info(f"IPM accepted create request for connection {connection}") + new_connection = self.apply_create_consistency(connection, lambda: self.get_connection_by_href(connection.href)) + if new_connection: + LOGGER.info(f"Created connection {new_connection}") + return new_connection.href + else: + LOGGER.error(f"Consistency failure for connection {connection}, result {resp}") + return None else: LOGGER.error(f"Create failure for connection {connection}, result {resp}") return None diff --git a/src/device/service/drivers/xr/cm/connection.py b/src/device/service/drivers/xr/cm/connection.py index 088c743d5..51e94db4a 100644 --- a/src/device/service/drivers/xr/cm/connection.py +++ b/src/device/service/drivers/xr/cm/connection.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations from typing import Dict, Optional from dataclasses import dataclass from .tf_service import TFService @@ -33,7 +34,6 @@ class CEndpoint: capacity: int href: Optional[str] - def ifname(self) -> str: if self.vlan is None: return self.module + "|" + self.port @@ -56,6 +56,45 @@ class CEndpoint: return cfg +@dataclass +class LifeCycleInfo: + # State is None (if not known), or one of the following (in future there might be more, so lets not assuem too much) + # 'pendingConfiguration': This state occurs when one of the network connection modules is pending configuration or pending deletion. + # 'configured': This state occurs when all network connection modules are configured. + # 'configurationFailed': This state may occur when at least a configuration of a module from this network connection failed or timeout. + # 'pendingDeletion': This state may occur when a request to delete this network connection is being processed. + # 'deletionFailed': This state may occur when at least a removal of a module from this network connection failed or timeout. + # 'networkConflict': This state may occur when there is a conflict in a network connection module configuration. + # 'deleted': This state occurs when a network connection is removed. + state: Optional[str] + reason: Optional[str] + + def is_terminal_state(self) -> bool: + if self.state is None: + return True + if self.state.endswith('Failed') or self.state.endswith('Conflict'): + return True + if self.state == "configured" or self.state == "deleted": + return True + return False + + def __str__(self): + state_str = "unknown" if self.state is None else self.state + if self.reason: + return f"({state_str} (reason: {self.reason})" + return state_str + + @staticmethod + def new_from_top_level_json(json_dict: Dict[str, any]) -> LifeCycleInfo: + if "state" not in json_dict: + return LifeCycleInfo(None, None) + state = json_dict["state"] + return LifeCycleInfo(state.get("lifecycleState", None), state.get("lifecycleReason", None)) + + @staticmethod + def new_unknown() -> LifeCycleInfo: + return LifeCycleInfo(None, "not yet communicated with IPM") + class ConnectionDeserializationError(Exception): pass @@ -91,6 +130,8 @@ class Connection: ep_mod_aip = get_endpoint_mod_aid(ep) if ep_mod_aip: self.endpoints.append(CEndpoint(*ep_mod_aip, None, get_endpoint_capacity(ep), ep["href"])) + + self.life_cycle_info = LifeCycleInfo.new_from_top_level_json(from_json) self.cm_data = from_json except KeyError as e: raise ConnectionDeserializationError(f"Missing mandatory key {str(e)}") from e @@ -113,6 +154,7 @@ class Connection: # String "none" has a special meaning for implicitTransportCapacity self.implicitTransportCapacity ="none" + self.life_cycle_info = LifeCycleInfo.new_unknown() self.cm_data = None else: # May support other initializations in future diff --git a/src/device/service/drivers/xr/cm/tests/test_connection.py b/src/device/service/drivers/xr/cm/tests/test_connection.py index cf1f9f874..bf3887dec 100644 --- a/src/device/service/drivers/xr/cm/tests/test_connection.py +++ b/src/device/service/drivers/xr/cm/tests/test_connection.py @@ -30,6 +30,8 @@ def test_connection_json(): assert connection.name == "FooBar123" assert "name: FooBar123, id: /network-connections/4505d5d3-b2f3-40b8-8ec2-4a5b28523c03, service-mode: XR-L1, end-points: [(XR LEAF 1|XR-T1, 0), (XR HUB 1|XR-T1, 0)]" == str(connection) + assert "configured" == str(connection.life_cycle_info) + assert connection.life_cycle_info.is_terminal_state() config = connection.create_config() expected_config = {'name': 'FooBar123', 'serviceMode': 'XR-L1', 'implicitTransportCapacity': 'portMode', 'endpoints': [{'selector': {'moduleIfSelectorByModuleName': {'moduleName': 'XR LEAF 1', 'moduleClientIfAid': 'XR-T1'}}}, {'selector': {'moduleIfSelectorByModuleName': {'moduleName': 'XR HUB 1', 'moduleClientIfAid': 'XR-T1'}}}]} @@ -94,6 +96,8 @@ def test_connection_from_service(): # Port mode connection = Connection(from_tf_service=TFService("FooBar123", "XR LEAF 1|XR-T1", "XR HUB 1|XR-T1", 0)) assert connection.create_config() == {'name': 'TF:FooBar123', 'serviceMode': 'XR-L1', 'implicitTransportCapacity': 'portMode', 'endpoints': [{'selector': {'moduleIfSelectorByModuleName': {'moduleName': 'XR LEAF 1', 'moduleClientIfAid': 'XR-T1'}}}, {'selector': {'moduleIfSelectorByModuleName': {'moduleName': 'XR HUB 1', 'moduleClientIfAid': 'XR-T1'}}}]} + assert '(unknown (reason: not yet communicated with IPM)' == str(connection.life_cycle_info) + assert connection.life_cycle_info.is_terminal_state() # VTI mode connection = Connection(from_tf_service=TFService("FooBar123", "XR LEAF 1|XR-T1.A", "XR HUB 1|XR-T1.100", 0)) diff --git a/src/tests/ofc22/descriptors_emulated_xr.json b/src/tests/ofc22/descriptors_emulated_xr.json index 4cb0dbfca..d6a2f0234 100644 --- a/src/tests/ofc22/descriptors_emulated_xr.json +++ b/src/tests/ofc22/descriptors_emulated_xr.json @@ -79,7 +79,7 @@ "device_config": {"config_rules": [ {"action": 1, "custom": {"resource_key": "_connect/address", "resource_value": "172.19.219.44"}}, {"action": 1, "custom": {"resource_key": "_connect/port", "resource_value": "443"}}, - {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": "{\"username\": \"xr-user-1\", \"password\": \"xr-user-1\", \"hub_module_name\": \"XR HUB 1\"}"}} + {"action": 1, "custom": {"resource_key": "_connect/settings", "resource_value": "{\"username\": \"xr-user-1\", \"password\": \"xr-user-1\", \"hub_module_name\": \"XR HUB 1\", \"consistency-mode\": \"lifecycle\"}"}} ]}, "device_operational_status": 1, "device_drivers": [6], -- GitLab From a6e99e6c2926216ddd6f2175c80f50f3ebd35898 Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Mon, 2 Jan 2023 10:44:33 +0000 Subject: [PATCH 109/353] Perf Eval: - added results --- .../results-perf-eval/MW/dev-drv-mw.png | Bin 0 -> 242793 bytes .../results-perf-eval/MW/generate.sh | 4 + .../results-perf-eval/MW/generate_plot.py | 69 ++++++ .../results-perf-eval/MW/srv-hlr-mw.png | Bin 0 -> 238243 bytes .../OpenConfig/dev-drv-openconfig.png | Bin 0 -> 250529 bytes .../results-perf-eval/OpenConfig/generate.sh | 5 + .../OpenConfig/generate_plot.py | 74 ++++++ .../OpenConfig/srv-hlr-openconfig-l2nm.png | Bin 0 -> 241882 bytes .../OpenConfig/srv-hlr-openconfig-l3nm.png | Bin 0 -> 241795 bytes .../results-perf-eval/TE/te-cdf.py | 30 +++ .../results-perf-eval/TE/te-perf-eval.png | Bin 0 -> 235617 bytes .../XR/dev-drv-xr-with-outliers.png | Bin 0 -> 237859 bytes .../results-perf-eval/XR/dev-drv-xr.png | Bin 0 -> 237646 bytes .../results-perf-eval/XR/generate.sh | 3 + .../results-perf-eval/XR/generate_plot.py | 61 +++++ .../emulated/exp1-dev-drv-emu-l2nm.png | Bin 0 -> 259814 bytes .../emulated/exp1-dev-drv-emu-l3nm.png | Bin 0 -> 261086 bytes .../emulated/exp1-dev-drv-tapi.png | Bin 0 -> 239103 bytes .../emulated/exp1-pathcomp-rpc-compute.png | Bin 0 -> 292111 bytes .../emulated/exp1-svc-hdl-l2nm-emu.png | Bin 0 -> 236416 bytes .../emulated/exp1-svc-hdl-l3nm-emu.png | Bin 0 -> 236896 bytes .../emulated/exp1-svc-hdl-tapi.png | Bin 0 -> 227650 bytes .../emulated/exp2-context-connection-rpcs.png | Bin 0 -> 261372 bytes .../emulated/exp2-context-device-rpcs.png | Bin 0 -> 233249 bytes .../emulated/exp2-context-link-rpcs.png | Bin 0 -> 209223 bytes .../emulated/exp2-context-service-rpcs.png | Bin 0 -> 250470 bytes .../emulated/exp2-context-slice-rpcs.png | Bin 0 -> 251757 bytes .../emulated/exp2-context-topology-rpcs.png | Bin 0 -> 240892 bytes .../emulated/exp2-device-driver-emu.png | Bin 0 -> 241939 bytes .../emulated/exp2-device-rpcs.png | Bin 0 -> 226063 bytes .../emulated/exp2-dlt-rpcs.png | Bin 0 -> 245962 bytes .../emulated/exp2-pathcomp-rpcs.png | Bin 0 -> 209173 bytes .../emulated/exp2-service-rpcs.png | Bin 0 -> 246799 bytes .../emulated/exp2-slice-rpcs.png | Bin 0 -> 234560 bytes .../emulated/exp2-svc-hdl-l2nm-emu.png | Bin 0 -> 236856 bytes .../emulated/exp2-svc-hdl-l3nm-emu.png | Bin 0 -> 236956 bytes .../results-perf-eval/emulated/generate.sh | 29 +++ .../emulated/generate_plot.py | 219 ++++++++++++++++++ 38 files changed, 494 insertions(+) create mode 100644 src/common/method_wrappers/results-perf-eval/MW/dev-drv-mw.png create mode 100755 src/common/method_wrappers/results-perf-eval/MW/generate.sh create mode 100644 src/common/method_wrappers/results-perf-eval/MW/generate_plot.py create mode 100644 src/common/method_wrappers/results-perf-eval/MW/srv-hlr-mw.png create mode 100644 src/common/method_wrappers/results-perf-eval/OpenConfig/dev-drv-openconfig.png create mode 100755 src/common/method_wrappers/results-perf-eval/OpenConfig/generate.sh create mode 100644 src/common/method_wrappers/results-perf-eval/OpenConfig/generate_plot.py create mode 100644 src/common/method_wrappers/results-perf-eval/OpenConfig/srv-hlr-openconfig-l2nm.png create mode 100644 src/common/method_wrappers/results-perf-eval/OpenConfig/srv-hlr-openconfig-l3nm.png create mode 100644 src/common/method_wrappers/results-perf-eval/TE/te-cdf.py create mode 100644 src/common/method_wrappers/results-perf-eval/TE/te-perf-eval.png create mode 100644 src/common/method_wrappers/results-perf-eval/XR/dev-drv-xr-with-outliers.png create mode 100644 src/common/method_wrappers/results-perf-eval/XR/dev-drv-xr.png create mode 100755 src/common/method_wrappers/results-perf-eval/XR/generate.sh create mode 100644 src/common/method_wrappers/results-perf-eval/XR/generate_plot.py create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp1-dev-drv-emu-l2nm.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp1-dev-drv-emu-l3nm.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp1-dev-drv-tapi.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp1-pathcomp-rpc-compute.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp1-svc-hdl-l2nm-emu.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp1-svc-hdl-l3nm-emu.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp1-svc-hdl-tapi.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-context-connection-rpcs.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-context-device-rpcs.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-context-link-rpcs.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-context-service-rpcs.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-context-slice-rpcs.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-context-topology-rpcs.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-device-driver-emu.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-device-rpcs.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-dlt-rpcs.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-pathcomp-rpcs.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-service-rpcs.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-slice-rpcs.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-svc-hdl-l2nm-emu.png create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/exp2-svc-hdl-l3nm-emu.png create mode 100755 src/common/method_wrappers/results-perf-eval/emulated/generate.sh create mode 100644 src/common/method_wrappers/results-perf-eval/emulated/generate_plot.py diff --git a/src/common/method_wrappers/results-perf-eval/MW/dev-drv-mw.png b/src/common/method_wrappers/results-perf-eval/MW/dev-drv-mw.png new file mode 100644 index 0000000000000000000000000000000000000000..a5732f8d162182a014497f219b510baa3d5ac105 GIT binary patch literal 242793 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

0C%60iKhK<~qH^29kpc9nyFEGL@bKXw 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�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 UzIR_SDzx1@m3Q3R_T#Vr9ghDJga7~l literal 0 HcmV?d00001 diff --git a/src/common/method_wrappers/results-perf-eval/MW/generate.sh b/src/common/method_wrappers/results-perf-eval/MW/generate.sh new file mode 100755 index 000000000..bb86e7475 --- /dev/null +++ b/src/common/method_wrappers/results-perf-eval/MW/generate.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +python generate_plot.py "DEVICE_DRIVER_MW" +python generate_plot.py "SERVICE_HANDLER_MW" diff --git a/src/common/method_wrappers/results-perf-eval/MW/generate_plot.py b/src/common/method_wrappers/results-perf-eval/MW/generate_plot.py new file mode 100644 index 000000000..beae663f6 --- /dev/null +++ b/src/common/method_wrappers/results-perf-eval/MW/generate_plot.py @@ -0,0 +1,69 @@ +import enum, sys +import numpy as np +import matplotlib.pyplot as plt + +class PlotName(enum.Enum): + DEVICE_DRIVER_MW = 'dev-drv-mw' + SERVICE_HANDLER_MW = 'srv-hlr-mw' + +plot_name = PlotName.__members__.get(sys.argv[1]) +if plot_name is None: raise Exception('Unsupported plot: {:s}'.format(str(plot_name))) + +PLOTS = { + PlotName.DEVICE_DRIVER_MW: ( + #'Device Driver - MicroWave', '0.0001-100', [ + # ('GetConfig', [0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,10,172,0,1,0,0,0,0,0,0]), + # ('SetConfig', [89,1,0,0,0,0,0,0,0,0,0,0,0,0,0,6,34,50,1,0,0,0,0,0,0,0]), + # ('DeleteConfig', [90,1,0,0,0,0,0,0,0,0,0,0,0,0,2,3,0,4,72,12,0,0,0,0,0,0]), + #]), + 'Device Driver - MicroWave', '0.1-10', [ + ('GetConfig', [0,1,0,10,172,0,1,0]), + ('SetConfig', [0,0,6,34,50,1,0,0]), + ('DeleteConfig', [0,2,3,0,4,72,12,0]), + ]), + PlotName.SERVICE_HANDLER_MW: ( + 'Service Handler - L2NM MicroWave', '1-100', [ + ('SetEndpoint', [0,1,0,1,5,75,6,0]), + ('DeleteEndpoint', [0,0,0,0,1,77,17,0]), + ]), +} + +BINS_RANGES = { + '0.0001-100' : [0, 0.0001, 0.00025, 0.0005, 0.00075, 0.001, 0.0025, 0.005, 0.0075, + 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, + 25, 50, 75, 100, 200], + '0.1-10' : [0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10], + '0.0001-1' : [0, 0.0001, 0.00025, 0.0005, 0.00075, 0.001, 0.0025, 0.005, 0.0075, + 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1], + '0.0001-0.25' : [0, 0.0001, 0.00025, 0.0005, 0.00075, 0.001, 0.0025, 0.005, 0.0075, + 0.01, 0.025, 0.05, 0.075, 0.1, 0.25], + '1-100' : [1, 2.5, 5, 7.5, 10, 25, 50, 75, 100], + '0.001-100' : [0, 0.001, 0.0025, 0.005, 0.0075, 0.01, 0.025, 0.05, 0.075, + 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, 25, 50, 75, 100, 200], + '0.001-7.5' : [0, 0.001, 0.0025, 0.005, 0.0075, 0.01, 0.025, 0.05, 0.075, + 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10], + '0.01-5' : [0, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5], +} + +# plot the cumulative histogram +fig, ax = plt.subplots(figsize=(8, 8)) + +bins = PLOTS[plot_name][1] +if isinstance(bins, str): bins = BINS_RANGES[PLOTS[plot_name][1]] +bins = np.array(bins).astype(float) + +for label, counts in PLOTS[plot_name][2]: + counts = np.array(counts).astype(float) + assert len(bins) == len(counts) + 1 + centroids = (bins[1:] + bins[:-1]) / 2 + ax.hist(centroids, bins=bins, weights=counts, range=(min(bins), max(bins)), density=True, + histtype='step', cumulative=True, label=label) + +ax.grid(True) +ax.legend(loc='upper left') +ax.set_title(PLOTS[plot_name][0]) +ax.set_xlabel('seconds') +ax.set_ylabel('Likelihood of occurrence') +plt.xscale('log') +plt.savefig('{:s}.png'.format(plot_name.value), dpi = (600)) +plt.show() diff --git a/src/common/method_wrappers/results-perf-eval/MW/srv-hlr-mw.png b/src/common/method_wrappers/results-perf-eval/MW/srv-hlr-mw.png new file mode 100644 index 0000000000000000000000000000000000000000..70368ade484fd07310b521b6ea8182b223604922 GIT binary patch literal 238243 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�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 literal 0 HcmV?d00001 diff --git a/src/common/method_wrappers/results-perf-eval/OpenConfig/dev-drv-openconfig.png b/src/common/method_wrappers/results-perf-eval/OpenConfig/dev-drv-openconfig.png new file mode 100644 index 0000000000000000000000000000000000000000..7130d5cc721a5b3dd419f0eba1217664aab064d4 GIT binary patch literal 250529 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%0DACH#Nr)H?)23B{H3UZTek$ezAx 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

  • 3v`^31!ym{2b! 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

    (;}F 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 literal 0 HcmV?d00001 diff --git a/src/common/method_wrappers/results-perf-eval/TE/te-cdf.py b/src/common/method_wrappers/results-perf-eval/TE/te-cdf.py new file mode 100644 index 000000000..60919ba8b --- /dev/null +++ b/src/common/method_wrappers/results-perf-eval/TE/te-cdf.py @@ -0,0 +1,30 @@ +import matplotlib.pyplot as plt + +flow_creation_us = [ + 3.007065,3.007783,3.010780,3.007374,3.006519,3.006668,3.006303,3.006463,3.006758,3.007992,3.012198,3.001413, + 3.007289,3.006241,3.007523,3.007569,3.006643,3.006255,3.007058,3.006111,3.006918,3.007972,3.006829,3.007378, + 3.007666,3.003071,3.006774,3.006060,3.006731,3.005812 +] + +flow_update_us = [ + 3.005123,3.004228,3.003897,3.006692,3.003767,3.003749,3.004626,3.004333,3.004449,3.003895,3.004092,3.003979, + 3.005099,3.213206,3.004625,3.004707,3.004187,3.004609,3.003885,3.004064,3.004308,3.004280,3.004423,3.211980, + 3.004138,3.004394,3.004018,3.004747,3.005719,3.003656 +] + +n_bins = 10 +fig, ax = plt.subplots(figsize=(8, 8)) + +# plot the cumulative histograms +n, bins, _ = ax.hist(flow_creation_us, n_bins, density=True, histtype='step', cumulative=True, label='FlowCreate') +print(n, bins) +n, bins, _ = ax.hist(flow_update_us, n_bins, density=True, histtype='step', cumulative=True, label='FlowUpdate') +print(n, bins) + +ax.grid(True) +ax.legend(loc='lower center') +ax.set_title('TE Flow Management Delay') +ax.set_xlabel('seconds') +ax.set_ylabel('Likelihood of occurrence') +plt.savefig('te-perf-eval.png', dpi = (600)) +plt.show() diff --git a/src/common/method_wrappers/results-perf-eval/TE/te-perf-eval.png b/src/common/method_wrappers/results-perf-eval/TE/te-perf-eval.png new file mode 100644 index 0000000000000000000000000000000000000000..5b2fd8160bddb4322d8fbb22dac950598bf2115b GIT binary patch literal 235617 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⪼C?q-NB~DyQ+UD5^wa!7SD# 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

    FIZ7ug%h)HbGIgCD}l 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⪙H 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

    }g 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

    #!dTeu{{|#Qul-2@4D(1rSu=2wu@DR&NJ>Z$ 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 literal 0 HcmV?d00001 diff --git a/src/common/method_wrappers/results-perf-eval/XR/generate.sh b/src/common/method_wrappers/results-perf-eval/XR/generate.sh new file mode 100755 index 000000000..eae3eb6d6 --- /dev/null +++ b/src/common/method_wrappers/results-perf-eval/XR/generate.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +python generate_plot.py "DEVICE_DRIVER_XR" diff --git a/src/common/method_wrappers/results-perf-eval/XR/generate_plot.py b/src/common/method_wrappers/results-perf-eval/XR/generate_plot.py new file mode 100644 index 000000000..66110b444 --- /dev/null +++ b/src/common/method_wrappers/results-perf-eval/XR/generate_plot.py @@ -0,0 +1,61 @@ +import enum, sys +import numpy as np +import matplotlib.pyplot as plt + +class PlotName(enum.Enum): + DEVICE_DRIVER_XR = 'dev-drv-xr' + +plot_name = PlotName.__members__.get(sys.argv[1]) +if plot_name is None: raise Exception('Unsupported plot: {:s}'.format(str(plot_name))) + +PLOTS = { + PlotName.DEVICE_DRIVER_XR: ( + #'Device Driver - XR', '0.0001-0.25', [ + # ('GetConfig', [0,0,0,0,0,0,0,0,0,77,1,1,0,0]), + # ('SetConfig', [0,15,17,7,0,0,0,0,0,0,34,3,2,0]), + # ('DeleteConfig', [23,16,0,0,0,0,0,0,0,1,32,5,1,0]), + #]), + 'Device Driver - XR', '0.0001-0.25', [ + ('GetConfig', [0,0,0,0,0,0,0,0,0,77,1,1,0,0]), + ('SetConfig', [0,0,0,0,0,0,0,0,0,0,34,3,2,0]), + ('DeleteConfig', [0,0,0,0,0,0,0,0,0,1,32,5,1,0]), + ]), +} + +BINS_RANGES = { + '0.0001-100' : [0, 0.0001, 0.00025, 0.0005, 0.00075, 0.001, 0.0025, 0.005, 0.0075, + 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, + 25, 50, 75, 100, 200], + '0.0001-1' : [0, 0.0001, 0.00025, 0.0005, 0.00075, 0.001, 0.0025, 0.005, 0.0075, + 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1], + '0.0001-0.25' : [0, 0.0001, 0.00025, 0.0005, 0.00075, 0.001, 0.0025, 0.005, 0.0075, + 0.01, 0.025, 0.05, 0.075, 0.1, 0.25], + '0.001-100' : [0, 0.001, 0.0025, 0.005, 0.0075, 0.01, 0.025, 0.05, 0.075, + 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10, 25, 50, 75, 100, 200], + '0.001-7.5' : [0, 0.001, 0.0025, 0.005, 0.0075, 0.01, 0.025, 0.05, 0.075, + 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10], + '0.01-5' : [0, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5], +} + +# plot the cumulative histogram +fig, ax = plt.subplots(figsize=(8, 8)) + +bins = PLOTS[plot_name][1] +if isinstance(bins, str): bins = BINS_RANGES[PLOTS[plot_name][1]] +bins = np.array(bins).astype(float) + +for label, counts in PLOTS[plot_name][2]: + counts = np.array(counts).astype(float) + assert len(bins) == len(counts) + 1 + centroids = (bins[1:] + bins[:-1]) / 2 + ax.hist(centroids, bins=bins, weights=counts, range=(min(bins), max(bins)), density=True, + histtype='step', cumulative=True, label=label) + +ax.grid(True) +ax.legend(loc='upper left') +ax.set_title(PLOTS[plot_name][0]) +ax.set_xlabel('seconds') +ax.set_ylabel('Likelihood of occurrence') +plt.xscale('log') +plt.savefig('{:s}.png'.format(plot_name.value), dpi = (600)) +plt.show() diff --git a/src/common/method_wrappers/results-perf-eval/emulated/exp1-dev-drv-emu-l2nm.png b/src/common/method_wrappers/results-perf-eval/emulated/exp1-dev-drv-emu-l2nm.png new file mode 100644 index 0000000000000000000000000000000000000000..84ab8747e2a6ca5153e4b6e02483030ab891a421 GIT binary patch literal 259814 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

    !wP0&! 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 &yS`R*VpRS>l)cvik@tzzzvd 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
    Q(hPJ3+ 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 Yx<4Au9i)Cr(26bA*4H|>aLu0o0nlHjh5!Hn literal 0 HcmV?d00001 diff --git a/src/common/method_wrappers/results-perf-eval/emulated/exp1-pathcomp-rpc-compute.png b/src/common/method_wrappers/results-perf-eval/emulated/exp1-pathcomp-rpc-compute.png new file mode 100644 index 0000000000000000000000000000000000000000..af84e7e9d5866671e9441c1a93b9d2a66a9218c9 GIT binary patch literal 292111 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�>@HS14S)^jwN^g0_u4z#pF63Z1kf?yKvMd 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 literal 0 HcmV?d00001 diff --git a/src/common/method_wrappers/results-perf-eval/emulated/exp1-svc-hdl-l3nm-emu.png b/src/common/method_wrappers/results-perf-eval/emulated/exp1-svc-hdl-l3nm-emu.png new file mode 100644 index 0000000000000000000000000000000000000000..317af9ca13267f313cab9e6d67a3ba068a3993a4 GIT binary patch literal 236896 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

    em+BfLt%E}mele=__l6_QA0q}!~bgmI0 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

    _G#;;hi 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 EAEb>bE&u=k literal 0 HcmV?d00001 diff --git a/src/common/method_wrappers/results-perf-eval/emulated/exp1-svc-hdl-tapi.png b/src/common/method_wrappers/results-perf-eval/emulated/exp1-svc-hdl-tapi.png new file mode 100644 index 0000000000000000000000000000000000000000..764b4f6cc64db88512b449e91b2ff31d6b260f91 GIT binary patch literal 227650 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

    DK60TSje=wtr=kT*9|0HZX4Dg$(bCf6@S zgI_nLtvI!JKjmjz9C$k7*F)@Q{{8p(y$_o{F;Xb1b?CDLVA8Va6gOG59X3%d8QiX| zbZxD+>wlFcivfil*8UycK-VIyjPWR+R^^ahxT(f4Sm(tJ}X#KYC4hA-ZIG zVn8$C)fZkz>*{{ra6S0VS3fCSIh1RJ7PH$+jPm^b8!nUMOuh>aA>iPx#mX8VyzhB6 z%N_Bf>^H}Yi{`fU^?BLuH%h;r+*e)GBB$I_Cu13Le$>2h^PpESxYND5Ew-{V`{t)% zez$&6UY=V7@pn}wjW9Q;_59_}s zg#FRKYk(i}q1GbiIQXC%M&LV&y9Nf85=@V;=)Z~Y1Z#6kPUHallqVvh*dX zAH-g~=wa;*Zzc~EUCPFWUtVy?{PWGYb?zm41s6Rc5!z|K72n9T9?z?%vI^i8#F{>> zJ*!&&xIwJ&_+&1+ zyGMT~`hA*kg;&vRDLBLi)}E5#pTz(My2W{E1t}rOc?FiZ{Vf0^Jh=ftFyLPJet()iWELEL2QE9mPpFR0~8*dz173tq& z<@ociiD4yuo+Cwv`vh*yovyNkqXDtDWoJ`WUqyJk#fKnuUlW(uS9mN}8@BYm(^fdSQ9d<{_{J&`?OvFQ6s>T3`zrZoxgjxE`3h{r(>#Lm&V5wcmaBI#pOuRC9=H*`4`cp-4F1PO@x ze<5((|8b%Y6rK?w&w@^fbwREWE29H_u@#cO55G#@3TKJXVW$WRKZ5kfBR&ObNl*_9 zhY!A#9NyhF-5U?l#4ttHCAqx?l16du|Na!fX^Hbl@oTwXi=3N@S2+p0uKS9 z|NY~1m^G3?Wm$Fr-g1j1w*$-jf1p2B2p?!4{LN(}bwD3WLHjbkZahX?7Iae1Z+22h zjs2gAz&(&vedBx4+Q>wH>>BGpDki>*{eJ#OQAbC3-1i92qFUE1Ba0(&X9cS0u?+v~ zqR|a_+gPDyjwt@&S4p?RgBxl>U&H+>Xe<@ejkM?+}_|0^7(V$1Yzub}yF;s#E4d2}vu?PJC8az&p z;6p}?HL(5X8VSc6afH%3|Bh$(w-@g7(YW<5#M&SE3_dppEs=EuJLGA8M01%Ha%?+Y zGw&(SNUn=L2;C^UdK`cbNYKMp>AyED=Jsu7)rI}40zVYCS#+L+N>Z`qG3XJ3&XQ{Gf)pZx1e9MT{r9!sefK(G2N77os}T?i zgtQng<6hE?I?}7d6Nro&QgOrQS?1ZmH%7DB!+{K2^0As^CFdm(a{yZN*?D$JRRssr{8TI8f z#YD9(%+`9)vLyH-v^t!Hpyl86B~_$!VlA}5)vVUQ4*UHER_yHQeOqdP)fqp}Vh6-W zUsR-2zH=D4_$17uY2zNV2T7=)i{cn=w&3VV3OX72s8k-4LBnw#lCwoR#`_-u4V!Q* z@Rj86_s{1NS#`KSW*~y~^_0M&QmM=buBya4aZ=6XFaW+Z+14KWX_+H3I4kT;f|Dra zt`XVha8OAjE}xzc&hwuh40zo2kAHtX7~h#C$^5;T-DwMXpc&vQo{R(^CL)jtpk(eI z$6;imlH-5(?eU$Nh`!PUt=%V zI_}p!4osbdcZsz7+m#>A-GGyvY*4nJwj%=g!rPSP>$Xn8w?sjRKw2DU$?g%A3fUIu zL8?%9nn58WSR#8CRsuffkSb(=Z&+oM+GU*m zQy51)5{#wXdga~4fB$&PIIaTc!U>M*z?z)n0D*-_B+(Zzs!(Xt0(kc-seB<4O9nYD zA-uPdu16(4HNh18svHwAt9LK%<2B6BWqZ6saP(%)xVPobBXzRhO8W${y$O6wqmD@- z(u6E@NVy`2!x49)aGUBBGR>MBurY-p3h0qxc1QOl`m*|kh_1ycIHMiTqCKHbf7kl08_C_#kp1w2>&jW1|7XBZm@^#o`q3Kgo=@RWl) zO!=D%dLO}(N>1$M1)N!=Ky3{Bg0yc5I`kNgXQ9J|cEVf}0*Fh@KW6lShs#!&Fh~QF z9fo#|oVIT86yG1dr<+-tFaTTsA4re>{8n>yw!Er2t6@Y;DW)6;OF*?pTU~dmIiNey)k2v=>@@3RLW)&$AbjHoe8K?|eqz z=_&Pg;OvkS&Aw`U1ZB6LqXi#din*}= z-CDcCT-iKDXUY_sR64z|4zI#V;)0LKILf+(&H3#Bw05TUv3!XZo zRXL482$PDA!&qe*9C{u`ZkW$;=9FAR8L7x=Mu>)c&|Ng|v_=iyU*K^`gSv}KGFspb zGF?)E^gxC*gR+Kp5Vxwx-eC8ajU130(62|+ej*2mprl(xMe>j#f~yF0034!TGWj=C z4KpySsfSKyGaBD(j-DEaN|!h<=Z3Irij9BT*_miC`7q=#AdUs3&HpPCn60vnDIXYM z`|2%m7~Q)tFMOlR69K-%-Zh2K4VfuN9Pr>d4TM}yk^w14xq&O@vJ<*SkPveemVC0M zl?{@zEC{^MQc!a1fy+$Hb`uUSrntC?;`VkKHABLO^ema!JFL?NiFOv)V_m0?3tPKvnVkG0oI_~zt$meS-LcpKb)nGQCh(6> z98^ZmFlUhQo#=Bf@Tvfl=lpJGit!-Or3Y(}7HqZbz!042slhL8kF zwY5OZ1N0SCUqn|BhnW}O=dEw=`7N{x_|=Vk7C`k2ID>=}jMB1dKn7uRiH$O>uMDJ; zV?ftAL0FbNrR7+S+56jE@HwUVsJ+KqAx(C-t6<|9fm)s>BL7E-Zhydeed<_?1%Bj6 zNlLVAY0^Sj4}CEn<7kup#YA=vEI0_I4`xQQ7|b%M&#aZySpmXDg$6n-HR#44_{{Jw zsgS&N1ig8LV^WB3rRDsAOMm(IaXJLhTnm9iXSAb-7P#=FH{ie?X?)ZAIG)3`0(#c(2?CLHN%7177{)jWNQ4u*#J|?4Gh5aOPkk{SVi$`f|_n!)(|(tRbN(4~R#@V$WTUY~#_7 zTx9`*yxucg9w{V};7`u~RO>nhmY@Qc5CO&T;94wi^7%MKPd}+Gk@+^=UN}gH&slbk z#k)}+r_iLG3bzw<)OK?^;$GZNTHxp|>cV0blsvm3%FS(cKzvOQrq)xx?S8~O4@Ve< zYd4T9>i#eem6c|{V|sNE?}lvaEyPy&QXEwgoMy@44FZMEdrUL>V43*ec+M>;I}lio zYxx>EharT?|CE%|&Ip_XW1&g~m=#|iI)EAvX1ZK`yOe4A0Z!-%#Oy3Y?KZJB&Ch+q98}P1d zS@8DB`}YLU4u%c*uW^IR4&dy`FfcThrsWU0hr8@%Z$Y0`P`@Cd&|P#LBP3_sep|r! zsJ95RhyJ;av=uHc3vifEyagCih^vN|RTvM+BBN$>K(2fwd04)b! zDVSGK2Mr$Ft9 zD0=3+Igi#9zumM1L0n%fr*-~p;y8-h=m@y0`4ds%vX4Ag?&zSXU%@Csac>F0zzdw< z&daVY6S5kPaDXk(3XAGf8PN zaxTNSlAUWPm8X<^v||F;10(}n0VYD17edLjYvS%FoeCkYrJ@T$A@ zRN2A0Bk7^Z9v&Pduwni}0LhqOdH^yy$s`O4cemHJ94fZ=xwl0o9+{VGa*i2p2>CRV zBQUH>ncZYzMg&gQmnlqXVjT?n6nf3ZejY85kb=LV{tiQe4@*zSf#+h{*6urP60QRSO(h=6+xakrOu@|@y z$O2{-KG|@S{wdgEuTQ${L6N{yhpuOXDVz1wC~IMmo+GP-MBYHngdGY>X9R~Re}q58jGptN-Ju5j#AEX*rR@Nf6;n&O-$@> z(a?=lVh5S3MmPeD;xh8EevVxsOwhxhgx|YvD2?cB!_x!2A#B+y z`onR1;F-ExXX0qB1fll~XR=5t+A5r6kR#eX2DcT?G9*L5kfPc`rVDH(fhOC2p7)WI zeBH+OP#}BmwR=S(@BXa2tNU)+G;T7@&-_%Pp^H66B>@s+MTrSc`YOmboB_>8K8&MB zAR`{nxF#Gh3kAP`+uX3^ieS`Bs{0{FbK&jLg4G$lnsbAQkFd18_Gs+}j~%;_!PS9N z-UMQyFachu6958glsIAI3TZo~qsQam#WD($x4KKS*dCZG3P-Pi-aGEET2ByJ_{jDq zPU{mnR>`!uA=tD4JK61*X(|Zi99qg@f|@E$nr$ErbXS^z9k^TYdczaY%_Xa#k~3$S z9MD`S@HTHPhf#`u{!>HXrUTuOt)~Qx)_)}P>gGO1KhhNE4n^zo^lAV!RGt3{qX<_g zHAY@VtE{KejEYLiae^GDV`Yv)^w}%WijcXs<4Fx`#c_9?j(}4J&QdrHZft6LsvCh1 zsNpvVST8Egp%!q=UL1vGLWzrv;Mssyy`&GK<8e3~4zheSUwAvaq@-;-TrjIZ9y@N* z^U5&j0=sqX7?3v(gOSxLK^hj1mOBVuOcu)OO{U*IcL9pkb<*YZs5x>ki&r5<=9I&r z3tx$1G3%XbcQ-j^G# z!FjRkpxjM6#9hJa5zjA5>zSdNz?I}KLiM6kLZvyM>~Lvm56di1j{g(rG+=gU;N|t4 zP#Xx8K{&a#@+zfjaoz~ZMU*v$&y0~Xp8}LT2^ENM zObvQ|8i#ZY=0krMxJ4dV_beup;QuM!VptUBF0vaHV}UcR38eG22a7#lplNXu$42z` zbJ(FRffCELl`anv_s>{HCCLF|eaDeO{eF_ZP% zlY!Id-giJ%=!>?gNZk7?2CuprCGPZ&S4A02XQh@~Y@gBF>_`hWB22D44{efc1iH`i z|40kO)YMt%-Hf|}@h(-MhHI?L2VaP&4Yr_t>OX9#hy;d?}d&6Vs{gGAl~Ne_-W z7y4+KcX7Ta=s_oGCALLpkx4#Ym{o32Z%3hjuX|!mbAa2bMe>eaW1Jj^*rNYBNnqE$ih=-nA$ z1$+0e95r?zH5ZH?r)|6uqaQ~aEb58PX@VyQFb_LmMjkISUFmQ{P^h$YOrJ*{^$qAw z@OJWplHZ($y3F6tb%|$R0=Am$z*&c2eU>df+f70e2;~V)uZy3i;DaKR1THjyUC!7KS9epW*W)SqGH_=zPq#tQs-|>|@z~{G3bERfuKW>m#%% z`DEpd$%MMIqmk;-I9oOX4LDU64z9+aBFyezCV4zWrZLnGTgxD9eZE?sJ&IDfEj>6Q zT)?aK>`aX1_(UFi@9cPTp1wJRyD+SXQVOUC`@9mLkar(|oEk$No{(SYFi_b=^EQU6*9eLI6|FBhG=4)Lb1|aJar~(?U#>>3y zeXw>~j(lHFb&mHN+BLnl>es?QtRTN;aDfgQsZsiIu3sENTlJfFej!nU6|7fGD@ou` z0TA-&)#f^Gd>wGYnnf4!P0Uw% zZq;h5oL6+)tE+q1H=qajwb$FmKupTbdgqLTaK?f^<|%+QLbch}W|E8OScwIBB`WG5 zbDKrR)(_O>{gZ%KfPYqFHypeUG;uySZuP~GJSbcXnfu8))(m(9TnB_zI5khtezJBg zD>OX)J@JXiU3sSSzHmHiykFE3Pk$C=X=T{CZmm6`bAk2wtjprj|gy%i6=#w}B^hgUT zo!Ph5PBRVO!wDM+e1>-xy;b6hInVw19j*y1$1r}gs%$@wne z51jc&W>z^+(LE~wgk3p{Z&8oA?`&xsDSg0e9%A+dVH3gaZV$YswKjR zq%XdL9L2yEjIHKGmqN!C3X{9a%BgX{F##$LAR;<|Wr1;{WaN)m$^JPPzcGR&gO~SQ zsV0&5Ji#4^&)toTEN$>b0Q&B>Pca>6Xc2oK?9zK%c&?>30AYjbyZHK*!2lvgUF<{N z;*L2BL7HZ;D1N772lyYZb=~6;p5{oM#ZAl7I)g`?@@i;&uq?Y7PEU98!Pxj<1&0M9 zW_p$AA#C8wP6R|>ly36=KJt7Z<@EJAl2C9&)ccT< zL?|DnYO(&UjF#4MN-VO`RigvFR*?)~RMQCVr?|q0NRe)?y!_N~)TL{1q|B}`9DyeQ zFOLD2Pnll}6=GF?e=)|Y-+$NfkQ3FidRg~=*8DB3RuC2Ah8B^uqmeYhduud&zfkxo z)+cC;=+%|p6kb4F6wN`oi0Jik!3CT0w@#SulU?gAM*gK$UI%DgHiFY-Bt@VJxWXc1 za7)!0tuHp$qr{V3^v6!*qy!h$&g}s$>~Z3BpgQTLFGwR(BFQ_eR=M_&HmE=#wc4M) zMvcI+pE!AgdOY<@1P`lr*^kbjct`<;+VL=mPXX%7A}g(vba}MSXEg5$O@w=xmj7UI zyp0Pw1$(PoufLZyc3a0M6cAW!_1+;!sl-NZKVS+=8@vm1 zC9bU-bhqerz?|m~ua68j?_|0Uf38b9*KZ3o28<3>0P)!C<*-|8<)61awx{fn7d{c2*A~OMiN$ zAXMRVdk?wQQ3`O-(HL|`LHj%~Sbg{YnOU|soUaMsN_0?}X!z>E@;A&)$!=5l!#9U? zOM&{i(h1+Mb&P`9iisXgDuxAT>UO54fK_7tY|8czcJ-xfTO2l;brj^j_xH&wop>-( zH>Xx?uK3b&H=dg$bxU=&hos^hubDd<*Ze9pqWA|etmr8N%GT;NA0+@5V3W(Xy32{e zQ9Zr$Ai?5MXqr-}aVU~_(W%ts@4GqKw%;jS4J>X3B9hl`xgNI*{i9Idkw@ag2mGqx z{qH+r13oc9fir#Yaukh<@skWR2Ha72xe8V`UwpLoV{w3fjZ>$Kc0FF8( z5=UH$*7aR^x7LPM*bRkK*37h=N$9qorygJe=0-;KQ;?HqtIKI}^9jl8JOD?T1>&P;lHj z?l`II^~x009Z3K-&cP*`zH16-wXJEFIX`C-a6gi1*4&x@Yr%B1O4w`S7);eH+1Qbt5jMZlJyd5!67~3 zS^(AfepepOOP{%Ssv#CX#i@Ih>OLv#g>Tt~1)WyBG8E7z8;oh41m^HWwaR-Lol$EP zD5Xw@fuIXCCh@s4pn4Q^B28W!wvYukq&jjDY@329q3a1Fw7{BJrm6eYAfclg>F-~w zWm%*l;IB2U0A6nrob=Ce)~gFTaReC!+xo<9jedl`B$5@KZmCO#gXA`_`(4Qn^=nFz z8F&gj%D`Q1LTLcoB2f6yl9^PX)OU2`rjpv}T<$DGTU2 znqPPV&|*6t8#t0?0WGKzvT)AP`p*19^1?-gse!YsTIU4e65*=Q^pgrv*d{RGDvH!B zZ=i2!FR&812sIi+JTfp;M|D%Sdvne{w35>aAslG|>Nvr%ExCe;_EH~>T3nqlq`ulJ z72Ai7GHHi!)SJRm3e`S^EU4O(@O$VjqfPC1b?x+)y&}>H@T_HNdSt(oTs`i)GW@N&~^)>u9Vi-$3D+=z5sgEr5!9HC@w#Vv}!W`LHH^q>=X(-_<{n(rIFF|YOl%RWAZCZ6@X-}@{* zb+D;YQ-&qj9HBXeJ5qSyvkjh02Q8e;{t>W~F_Ee0%uz){dbUOz;YNxdXv#|K(=+Ia zEQiJfa~9667%w(bLj#AyxPttOq24A#1QP~##-|)?s$^Sn6yag-hH0mXvr2k+brZRCX3Rwb5- zxHJVZMGat}FMx>_AYAFl;B}Q6;fQ-2(8-Y&t{7Ut&d#4MzwJ76Jtrr(YXWx=ol2jZ}z!XBGp7@g|<3ZPEG(ocP_pLDCik@K}|tnb~aJjF$@(OK`tP2VeSt4QAEs0PZUZ{0R{(8 zGWvBCDPh>M8=fr;CG(609Ju>oX>sGh;2m0F*ZVd^O&z{b-WF}((z3H1s6uCZbZ%qs z#my%ps0k^uY<&_cfdF_w$d|6z2ji$~=wQl4d+dM}ykNLnsJzinS0v9SsSdvM z=}$k#)}^WXRmh}P)X&_r)#k@1wRLH=^L~@@wW{CmkoTiXjeXyOMjXf)2+#*?{qb?@ zU#BD{CURRHux-ra0|$5>^kg1kNII9HM_RbcYOaz}CI&}@UyY1Bjh3>3$nqEvYbWN3ZDCh6f3ro}T12SE7~tq*~=>4yy{%JUTl&jh_niUCw!_j{*^OX(dLI zR8-HQHUELeJrc=rHpWP_s(&-;==T#iG>h|5%8lp4rL;ZUFeNspCZ@T}ZVy-fW2{-J zkWWNY2Yu3y>ynd@n3#z^*>IBX$Y5npp8gC3FbnN4Ct?AOedwU)mAfAnWSJJK&E5x! z*`*_a^Twa9n)D$sKG2E+KMu?&tGUkK@{B zEKH7!oK9sKeq<9{IP^HnYshnFD0i_}x5uG&136v`(ATEBfN|n{l|tO#am4qME!loYo%*4={>>x_;-9SP??-kU9lh&&o&ItcBEPP^tT<%^ z61RPbCHs=U*0v+HY#mga0yUOS3+SvQS5CN9J4vQvC0WI6Dzx2B#bf6}z5xuA6UR8i zfdZ93_az;uQtGf*1=vv*I%>y`9jqCe?&>(sP6UnjxBVoK!h@@UQqjaoalEyFFv3;}BFK zC!RllJ}wF^-c9T~aatN5;IXa|1C9!zmQAIK`C5h>Hf)G5zRAE3@2ke) z(L~z5YD|Umgr&dKy{_3{WW+HU$6@9Yrg!cJ9AoB?M)hQzvLo(oCVD3}?VZDiTtnK& z-z4*$BqC}l+}+*len16LTjjZ7_i>q?7b}|3OR`?@8wtFa9Z=%YR65YVr_LfEHg?~! zMyb=MPYY8{*hFKp#10&et}C&zvmxO=aHA1B`hIAIh3K%~l7315K^J!PccX=S#rI-P zbtRkVjrKLl=W@3tSD(VVrV~D=P0~62K(P|y8UPU%S!;}pMsq8Vg`jASp5CxWj~=Zt zHXesS1bXHOY*x-Q$UsD()|#Bsslx|6dGocdH}2|V2v(XrqU~kN@MWBgzvNUZ>a@bb z!>g)f`VKPc;QN5!U^zG=1%!smj~+dm&cgHxIS5h{UzL^sHeF^N2qvRwY*%hv`0(Ly zc3U|3oaZzPC4eyA5=UcWN(xRyc{UB$jJgNkHh$msqy}o=GlHFPUxh5C z`DHgqC#P`o-L7d34i4t@zRD?T9pZ;YMVaXMazf|1{2JaIBn*Qo*&$+67_AP|{)!oi z3E?yyz(5KYG{Bb|8yhEE0_GeIOyxKpEI(1jz=& zxwKc&ux{3&z&EFJCN=DK%KfD4t$2=>VcCrTyQZfWhog(rbm!|N zTCrPBHfklZBE?r@nGkUfmO#WoA8S#|oDGRPj|&VO967uF&Zy*KYnkD&;8Atp26aNG z>7nVBCqHwh3-}lTg%5%n&gRz$v;y8v2Zx>mZ6gO9J%}zHr}sRKw4(nxKLHQ|)#QYH zCsuOg9kTJdV(HSQ$7(r19NzZM7}r%-l*Fxy?=ZOi?(X{EqI@u3l1xe3b&Me3c4hDf zSn{QufgRBs|2^jH%+{|}^n$CtU8gRY+5KtfV@Zae=tU6xOR8w+z}?FcWU?w<$oNk& zBcP-Ds*EkI+d}*u50UaoH7Gf)Csp!h@B+Ct5QMnD)GwMN91G`wa6F(s+VpUJNptRd zx9#1h3!b2hPU{LT;OiG3jFRcRe~cCD89IPr^A}Zj$za&iUQ#U3eoeM-#0|?d0X>E) z0N9a7<+Sxxuqfg=9{{A`iQ@?6wFn-h*XcOC(z|#9lY#3bu^Hq4Ui*U;mb^8tl*4=I z1Srs}5scijqI*}U`tvDGa%}dgNQc_ze0MiWs4Ax0f7UddaB+MaAozBXmW+f%oS#>{ zkM!Y&R#jZ{NyM>fD6kW{mevQ8+(=mv?Df}^rt-41y1JU?W9nk@%u~B#3wfg%*kKPD zp3^pC`t;Kat)4UgF0Zz045MeJe_waa)u^b30tbGmgXmGx7|-}-qtj^c&IMOTv;q&5 zkCK*_7Tm7@kxcZeU67~Q)|Ea*bqFWdFptgvyob@tu(}I;;z9Vn>hyILzwi)_fe5>1=|Ng6LIO>;c4a zB;y!_ijwPXVOOs@lfXqL^s%w~R*uvvP5}=;lXU=nKa&@qT>vxmb%{IIv&3`kvpdgL z6#~WxrsD{cBkabC4sR`Zp6~$}d06FpC7;s(9&GxMZ;%jdh7w3HQKSX}cOKy2igajR zl2z&orAc`w-_d)HMJ5SsTfT$(hW| z%nu+^sftSU1 zwmImu!o&wNYL=oZ{Fd>N3JqorpyiF_EdVQsWG4abBSOi|05`A~YkrI-YlJj8&R-=i ziKy?C0hsnmN{Z?QAD;l|h}NuMKMIJ+2@b(UI*)Q&Zn?{NWB0a8nUO7PuDuLuE!O|) zViBt+);4%!1>OR=nBGI8RIN#CJl9qWHQ{W>3_x(v!>6A}{EpmyNT9KO9WvE*;U z`o9jj7#sQNt$+Q8@b4vG89J;?Kb3Vt<%_xtua=%r5w7;N>gOMhU2pYhIbPEEyD3ag z@pWhXyKV~w)a#;MN1oPJw3fEbR*cDwdkhm zbVbFJT5@jNM~g0n4ErF#K0H1Oit{I3V@8i2s!bM$a?yhp$%^9ZqB~F3KU^$_Zk$Q^ z<}r#*l-|dKvFi@k&r?!bjyFoS#JxD&H~B-HRAu7~ui77Saa_1<9*S2!=yWk@M*Y2( z>7tmn53_uH2qeVkaufRuF2NCPISSD$$MG9~^jYrSeFlG$m@Ma$%Mp1)wLcfV{YSrx z{te0D)rg3lHrkh0I|CP9hLCe56uI^(fBDIN04zv6@E3=l_Co{?{fI+i zQ1RTka|OTR>C>l6;bawU-b3_=W*qn%>l;e``BTpSOY}m!--92C_a<^~eIp97opR?f z^LL*rDk}Qi4L2#~x@6a`)3}CAjGsX8Px-BdX7DKE_}90^l4~EDoGtRayOmr!+87Ng z2JD&%P=6FBQXuIeuF#qpRcz= zseG_kmD#_4|3xE{sl!c?DPKcdY`5RPMTf`Xnw~s!kd>8{n)0JH@QjJ&{||fb9+qSJ zwhiBA9%hEYghU~ekQSO0NvVv|gb>nFQCcZdNh&RdnUPWmQCd(|l}hV%G1G$9Nu{+Z zDn+TZUcLKqo9B7PeerJFw|(39e((2w^~djLxV!G_I@OZg!T`j1u7EJYQLcW{luEh^~YV0e)F9Lw_F?c0JhB7@@~(l5kPoW0^Qoy-6F z$=C%#IZW&6euX$pp;@8!8@812^n&qai^ywMx zXW^aD-~;(^DQRgHARzR<|9S23?=dRi%(!_?WYwy3NCt$kZ1_LABF$*T;EEhBqW>qP zFu=NcgK7~mVQIwhx;>=0VIRx^2B{kuWYxbz9C+BmqI~v$e3w3+FDcR1r5T@P(zIBj zt#tETwd~G(lQ`_x(A&3fzX)ZqoPPK;yL&MM*xz8t8kQp0yy?H29XaCMSW8qHKXDz; z^08D>{;S5jY7m2$FCvURH~!b_`|m$E@qBu{kN@H`=My{k@h_LIn*8YG3QVrRG>3QVrRG>3QVrRvR}8p-~Lq(m$CD+*~CuIVtx0&sBASpTXtZ{Z98pw zmr)%>E#IkJKkVA{-fiXgKg|1n&+b_|zWcO%?q4?%KBw&!>L;_$_v+bc-$}|IzN32B zcfNb_{o{%krz&w>+4JksUmoQD`DfFgS zTBsG_|LgrSc!2*C+khq?YjOo9S734lCRbo`1twQuas?(=U~&Z}S734lCRbo`1twQu zas~b`uE5?KhM*+tS3(~-ai37H{Qa=7bLas_U%e*;_O^iOx(S55-o$_~;-^-O88<^P z4XLAd9&?dI^&o^^^JmYVwLvA22C+%3xeddGF~QWQ|L-3~$rWx>AITH*D0>+D!<+Kv ztD5@fAe1NNmTDmx$UHB591YWy-x<^#r-@&-G65-xf!Tm09qNajAyh#sCwUm` z{ChnpU_)Ie#;3Zh`uF$IpT*8$6ilt53%p^;AOj(Gi6qw&JXq76Yx*8U@t--6(Un7b zG?+W~B&L2h@guw#Lu4x~uG(`q8w`^DZQeBShZ{Bm?G*9)=V8LG+211S9N2AsB6jIk z9?t2v(r59){a>?gO3)B8@mH8a5OIJztL7>u>XH)=#wy3dY zxUC=(c#?0*!8q;=n+f8U0hg;a#w6#x8h;KAb+%>!4Qf9<(LiJbm>N{VL`iHQ-QK@{ zr1&w^Bee>QSFR-3iZSEqsrPQ(`jKQAKv(b${;L#RIP*w$2Hc^`8^=gXLfbTu8#9Ja z?KzkBp%uoQZayV>Dm_farEzBwyD*z=0h1e;Q}-rLUn1ThwK|$v#I{Gw3$cVb^)CDOgVLW%ze-qz7r#`%5DaWYCv*~|F>6Y&gT!6H<(&tSo(akk!)>55(q@y{UX!Zsb!252%02=hqj->Jv3hC=Uh{_`S&$q zK%&USMVF>Q?cPFSAR|_8$o`_11xehL~Kc>+h}muCK3;w3}bc+0%$F z2LGG>;5%w2<&q#m>M*)TQ`kZbD&SGH4ax%HhauDf(wt#;KK&89fupLNMB=*}><+j1 z&31WzrkivDoRtFv1V}-R*ojFptGA9+Qy})>Y+%n*jxlvEux#ebm4oexmT?gl$tm5G z78Y!-WQ$r-Xv!IU7y~E&EvU=>elbt_+5t%DN@6&OFVG%Bz(g_z16Uh6B73~;2fRg3 z2tZ0Z-o*e|W?!V$yRxyI_oR2x>PH+cG$)+VP=SG{!HQ^?tJfHM*$`vHS0oa*5r`8c zOGuQ9%w$Fl=zf9+!S&+j&;q&vXOIk=MEBn=EiL^4&r&QWNfbg*>}ohcOl`#2U`%4Q zrag(AkMZ%uhWKOUa707P957ACCQQ&jK|(sjBe$hh_QYFvkRG-eYyKDs9(tS)$&yoG zE+`pk(3_CKI8+Wu5iSj6eEMEkEGY$?-g!l2H9^gnFW388+*O8@;IBD@b>kfAAM_St zJZpiheP>}o=Fi#~h;K;;$%_Q&3e&q5gCPPlK!<{~p$XZ*G-IO}lG-Ij!*3cI6>aJ9 z@lvbP-~XB3830DdcsQ7{A)1i`r@E!SeM>FKeF9|^pcD}TxlxD4TIk8OfrYTe4-6Fw z4vn7@fU;PMz007YTP6pK=+_Gt6IpO5U#|*evn`)`wam}dCL`hn%>A0y-`x$yg2BV^ zlWit^(dl=bvh_EIFxGg}jFf9+6yR}Y#7b9XfFSkwrv8=!9Kv9kE>MF!KGB+*1~RUx zwI8b}|Gk!qy+7hk)M=SP?dk7e0!iJe)$n6&g+pM;nS+t^tQoPg{HZ72`O7ro{L-al zt7`xI3LpQp^k>*+0YBKj77{k1jdc2LCoBeeUq4gukr`a9A39bjkp~(U)zDtp9Ue>j zs5H#qzyH!x34xJFvh~b2vsbr`6YYa7(Wjs8Y~&->a&Rh0Z*T>Rj3J0J92(Q$nNEU2 zfiI9B$3*5YL(Uugfu8dcF(TN?V;t^uXNjHb9yVvuhfw~afC=oLrz7CA87lGXy|hfF z#d#&?6^bs?fgw5qn3Rr@=u^umI8epg#xvGJ>4IdZLwiA$m51})n`i}|6$ub$ z1Xyu;Nw<2s9UF|<#o*NyN~V0J;UrQumeQbUNt2r3-*O6L>T6++Y5ET^Tw z{AyutyKrvW$!DZgq$N+JbnFeLNxqE|1@)|;uIK4`#&AU|JDXgh`_wNss=>mT5^+z2$31Eq&yC!FNQY)wY@6 zBgc~`Pdc;PYw*oZS6(b( z@I}~M%gV%?zYopCL6;}^lbF|dszNA>RdlvXp5^BI*q)!ISnx7!Hp{Giot4&HXWIZa zHK|6$-FYR|rbC@yv&#~yThrp2}yaba)j zQf$ewXZ9yV=a3r&zbi@lzIr!K;jmrRexBf=LemgfUx&1$QX*Rc6Qx+qIG$q*n83>V zH?JN){}n6#`^*s*OJ`c6GlJwZxX+tmFB0u6iX~xHy{0v;cFO+et}nC{Hu6<#zO74% zd$E({HvdRtm?o>#(y}pgqiw@oHGg@dsEsKN)q591eW=^Z>eMn)sR8v^n|`A-^jT>Q zNg@z5I7vG(Ps{OR=08uA^~(-c#4(zzCNkCIVE2~qKDRR)fkgjsF})d3oXsKj#?dC8 zEO(0RW{ZESsezH#Kf*>Q%$EU~k6;1eJkXRD9h#=bbVl4?Ut6441kn!%m8pj1Ql zgB#up^mwFYI2R(rCXWZ${1yLt>eA6eJioARvM6bg*~$ILk%Gfktl&IiQ6ds5ir0$c zNGFxeum|eu1yW|a5Em_nYv6?;BT_mVLPw;W6Ch*T1g$z^rZaAk2HyvL7L=*kP*n+z z=3mt{3$%sn@lK{^nu6j*T~l*FA*%r#c9gF@9@xB^Xv45<<0a9=2#fc&>o=Y$tXGK8o#es+4ic->#fLd-%h!id|PPB zFCb2kv(1Fgk6_n}3Wc9Wtx{lyG59V4HR6Eo2-h#0on8n>3MGj$l`CV+N$f9eQAAk8NQ+CaQ1hUm z%}O?_=W28|+ikQE3tu`^gdJ)>nWM+fRqSQE4LIC_fU1fLZn@JM*nkK)O61D-e$AGy zZyCL~dDb~1igIXl0x78yoEPF;SfejRWHv}=%!(z!d?4P&ccOK?-of-RtuB0Wp(I7s zp@;*!(hBnPn;^u6n6q^AY;HMy&Y*Nhg!XaJ_%IVYZ_(8E055S{r2z%P7I6!7XdS%B%Q3KosI5ulq zK}jw)$VVxvi~)$h9yk8WT(JN6D3kK>kOgulkF}fv{od4i18r?T8Pf8Zp6a^uNL{jo ztqaoV`J*#aPP}qtWTX^e&rA7=EjR?UW!@osFhr2-M(_4LGY=3;s$VZwf}4K*Q^o@N z`itxgi72qQfU+>O7gR!|PBT5$36gFI32NCf`ybyg+;aKA(5UN}d#iY%}p>xEX*}e!Y;Io;S)F zn6uv2qU%e@pZL6N^}*Uya810VqZrOe2I#=EwscTUw5EBy0WEq#05*2oODp*LCjqUW zUuY@P!43d=%@D<2owaVGz^v$!JAo98Z(-W;;oLed0 zl2)ujnP>kzC{0-%*VonMBQZh!6Z>7q`Uub4(n{Ex=jv1;@wDl$!L03tV9Pit1waGQ zNjfxUK$q(z)EC#301#U_?fV562kJS$MoOH%Mg#lh)SGPvIKBviv-!v0pUtR|B&J3v z6-t)qK_$BkrYfTbTy*}x5uc7$i6zy=YRb|HEp)&pJ=MM^*aHN7rqTVQN#qRRwTyrt z__ZRSxR%5OnhfZB>q(*>4ip*m!9RZkOw_QXUI7e$1gL$yYg6}#Fe1YR@rX%~19&5w z>ugKN@Mbh5;c?j%!fa5X3PD&hs8RuBEq(;F5i>E|cvnX!@rtr3 zi2<8Qmej)*6DRzDoqf#lt_J5M!~LYiK*|q@AmZ}UumId(6{(3Fd;v^!A-rNu^M1}% zjbDVPllqQ`rS**m~?m58BUfF!%6nk3AyYK;Ro zJl}walPz`KnnFBn=Jx{U6HPFY55or9jtAk~4nm`F9!V*eLBs*Os4F1r9@IQYbhxv! zmh&LrR3pr2qLsV{g?YZ>1`Ta~z}e^xH$#!&JZ<(rX7ev0dVWN)Du_V|Tq2ThQd_jQ zXqCuYG?!wpOqbsd;g~m-sqlMYFH>S?8^q?z_cIM5htIqX9fnn4S5k$u}PTOq|G=Zx>=?$Zr#{3IpD&-c1;>M#X6!6geiykFy}YMe;?^F z2E|V+$~ZyDLL7S2Q(K8pk^T!DlrsQ;7H{ZyK)lJ=9~*}{iBcXq0Sk#ovZtQ&ifof* zX5$S(*6Tnb1CYK=O__KQSL^}eQy#h4tN9=qB%W#ON^R5SwK4#bh{p);bmeFKJ7vh1 zj}syZmbxa*tYHnh2XXro_+PdRD#Wdhch<>>+ppuMc#Sp;G&Gm23^?}l<9>Q2OF;2` z9c-_#2=@+f@PD&dW(~CB&xk({hspyOR4z0{(^Tw<3!k`zwK5#-i0{(DwH}t^+Q4-anhk`P|YREAx;vagVLQwqE2H>oXW@cu;Bt}cnfglVY z=Zy!-2n;CfgF zBWSh}z0a%QZ#mwnTMO8J+??~(RTguT*j`LJ3BNwjQS)7Au|>qzbN-Rl+l(%6xuKq_ z_p|X*&jo*e`}E+iI^JeRy#X%WMnPYnP0w`wkkQrUY};AhUt!x&)!owlqSLxQxT7Ug zU)uG3>&FQ=*6vRy;64UV7daRd*?;S{_jAj3`?Dm4SCKU`e!@PTJNx+WzkD`g6aL)L z;^eJPt|~JVnB1$A$BMHd0^MDc#_DsK@T6V++|2f*xB8rFz+?>YiCV*CxI!y1X;&xX z)z9FrlUcxI!opdmfOy5EW1V!YAN}UP|927x_*C(Ny*87Mb<(j;I@W)i3HF~ACo|T` zjFqt~CQ<9j$Z9gOVjhdh$m;)QWTl}}@il8p+^@g>y8gslt$_8`0>i_@O=$R1_cb0H zKP&&?*7Pby2_jt{HMG%a>ZQeG0~dP;6{FME5Dad6_ik}dud;eJruXLZFfye0Z>EU8 zOi#i%%?n7h(lG9{Y17(UjkRZW*kT%BE`~UcI9QENqdBdJ>% z-Sf?mKIW2Ge^O&!&PCUOitHGZ!s}erLyHm2*{K6j_8Hrl>$BeKe3&CkF>#rScXo)f zry;4SL!n-%U`>AP`4Az0eJ=H^szJwuyE}uIi0pe{HWVnH{{H8;4H-_!E&`le@X>vY zzXxc#V0s}NQyYdO{qOp&SGdm5pP5wR(jTW*@?fL1KoiZgKUCmsL}wk4LM^eFPhs%! zY(wa>bw|67iR8yV_~t(E8BMQ);rlLySGD&Ph+jZk|J_wl8nq8GxUk--|Lwz#4E+B< zJY8xA4Uk+F(ago@;ToY+++1q6B664RYc~C(04=z?vyP5-Vk9fCvp(%?pEl>CutG1f z=9l)9_%n$gUoozpm*TNtQD%Evn>z`w2ii9*q*-ss^vcL%&aX;Ac9A^#VO2~`O$)l} z&{F$Qj&o8BkJxeFk@e^o$D!BP*t+#-jP>n~s2N!(X_D=~*1|BygIfZVe|NjqV+7;j z(bV^U&M-oUWB04eJ7-Nla0CNMnZp&)vpRYix4Y$rgvM;v0#$>de9a~_i>t*%gb0NQ zdEiCTpboWBH~-F~?_Lq1Iord)>xxBmM&cXiBPD3qZNkjjDn7;)!v)-?#zzJP1toW7 zj!esmK3qjA(w4P}VwrvdAnE0rvMDyvioI8hq zd_Z%IS978fE1r~cT#GWP<1V~-y5pqtr$rKPCL zD=c8wox`(Id|?f2l5Wtzz`#mz#(Fc^CMD(?4>; z{_AFzUc*W8#k3Zl+~X%UNc?@n`(S*|GvM4KlvT}hufu< zbnj77Q9=c=ZytyiyofX1sqE?B_u)e}sc>^in3-y7YI0xZ{6*F?jD;L|6WDdibSzc0 zK*&QtKwuWX{36$hk;VvrjU)_;{@6u()tdQK^7K@&!d6e5Xy$kBwfKGv%+7MK0uRW! zZH#Z~p#$;e4FBkXg5FqBVqbV_;lEh!Sjf@w45hFTgy1~QG?-~C2+jm@CXHM_gxN&0) zhJ6++Snx}2Z=qyKwyAutpO8n0OJ%YJTv!Zdtm8K_?m&Zmo?&*HF9(kv+A|m~THxq* zIL~`=4CXG#^toGAXSupk5A^WJNQW#hHx40KI^4S$ms&8BR^kLE!18W1e7NiG?l>4e z|FLkvniHLM3wv8jF3?!xNv^&nQXnWkMa3oeL3g(f5Z z5v_C#C|nSUN*34=EWh!UJvKjvM#{>|=c;%wI%gW>umX%H+{7CZu)!{T{DIESOe;Jg9Wf#Y}q#WQBh4;~oBIIsuiND|K@pjb zO;C{Cpfe#|vmkZDz=%bcXNWt-wq+nk-0Sbk7!`Tc@$`E5H>^VoFmpEAu9@XA`_#*? z``R>&u7`I&nE&MPshO|qO2(49Jj}cg?#EP2Hw_>RS~0fvf+KjLy}9sJ3ut^5!?Clc z?^e#fxl5zjLa5axN<%B6J1(J@?qrG6+ZasI%=lx>o)KG}Kk=2;BL`y)Sa)N7Q54e; ze2W(^#-0_>OgKiDij=PvKBX~F`HJyE{DU<{RCD2A+DjNWw7wCBp*1i>sHwnU+-4Mm zo1O>&s?KO6?9wp$z9T^{RN|C|uQ4%|bU)v-GlwVh{qF|{xfzQdjk%pi4st`i$cf*n~ zK6~+hVdc6)y$>Eo>ZhN6I6G_cVFxSBD0O3l$l8^VuNfN~6YDDSUn`P(zHB+en$}>p zsmYc;R16>zFgxaIs-9v;Z z`0VygN?-0=PVXpqFneXYEU!HUon#e0s4-T$noMsWO!#e={l3QK8MQ~##GH22gE*(7 z$}0`|1OLQep2l{@*`%hw_{fz(CqrEJ&r=?AL=;?BU%x-+w!3>>g%PIn*e{TMUtPmTZ6cp?Tvh6jV4&v_QB+e3biv`H}l#UyNQU5@831@UtRXLNhmxUpc^soezO5AI_xRkJneh76)5}kp;R`!{Fu(8qoUd^(x9-P@oL#!i z9yGTT>^Lk|6hWbk?FvM1$Zd6k2KxGze<|KPH+N;CoPu?e68p=I>yoX%^yP16TAkcn z(3>^-TKws@KTf=Qa>=(96W{rZ8C&h*c7!b35I>qjWKx7gf?NH*xd_Kb!niU=6O|ImQu-FFO&f8T@df(~Br_=kHPG3-z+)vkj$y4;b! za@A6_Te7L=7MtI#w98Q8w{tea(kL+W{Kxvj#2B%-T4{EpCy7$%>?$D6==P zEB<@kwbR1b%@wLS5fV~A z>s+M7teC)a=hQb%Tl;;;_Mi7EK4fjWYv}6wDq<`5+Fzenr>(c!Z}Cn!u+^d1c4C%V z+va@(;^14122YCF_Y2nu4i9wkBNmTz6cE73X+#jMW=C|^lIVuPu_bHi6}ZKBJoODegMNe#m(^8fU_|M&VmN(>i7N})|mOB6v#9B(Vt`CXH1LNvp} zeM7;+bpv5tV^_nYi3JMmT067Bb#(11gOP%zXnPB1NrFi02ptQs$zQcLKOE#^PY!Pj zR_hAe>)b_DOQylj>+l(awhN4hDt#-2hq{7LK4;klxj7(Wa0YS1hG$Zd8v5MbC0|99 zjO-0}vG>1Qh9EVW$cR8w<^hzcrJD)3T$G)vrRaHCQ1#S6TB8@tYA$jL?a=t}s5K8t zY+d_gg}W5MsAxpohdJDuQcqj|drAKJ(~Ety$PYl9AWqKz1zGd82t-w?*)&z*(hI7_d>CLUuvsI(;cE!%1 zMMNC}NP}n_h%$+N4iFFv2!vqQ(Omiq%z}qt%sU2QO3tQl7SAJ2n;n50UXPkHzQB%hz#>UPs3;KUnz7WKjJ+n*((^CaPRXHK#(W3#e300Fg$n&s)pxB zw+m;6>z`-biQ?Zl;~q7{eE{;H6B#+0H+=OgG(Pjea%_uvk;K1a<55b-%0stLlCVYL$W%@(6kUlUSNWm@&svf>#pdp@l7>4frl8qaP zd}94ex37CV7!Oq%LU;Kr*0`YQ>B`^-!fD}qGrqS#Nx+&g7UY|LDoUJh#BKhzDf4$- zA+Vf0w%4B#`N5Y3&n@#_FF=21242i!Rfbd43Gzw*nlj6r?R*3^6uQ7aLcF;m zSqw|^X7tVs|7TS&dw^sk2Ij^cuHu%p%@I6DnG45fh0PT&#$mr~%fEP3@T*-K*1AA!M zpChfgq!3tbD0-cXj#1$$*j>q656*x)#sOTDPj;Hx&rT~ zoG+TS3mfweDkgc~tqk}92Sdvs>F9nrY)+Xw%;owSn^^^C`Y12knK!f z<|7!Uv&~14Fb;AYt--GHdB-__hb49a-=v(3x=nM|#7Gtxp5hOSsrn28S8YN(>j~DL zY|z`;Zs(*%05Kv(0OyGh4NdLF{?yD$B4Y|}bZBVIByqaIogo#tGx2;r3QZT@$;x-UEn%eTF1!NtYz{6YN` z%;n66vMbM|G?+du{#)9tni;=yJT1CCBV}W?@A^MC4ETJ|B3vYkkT({9(I6nvIVcgGBi2b`m8NQm5dlfa zv~y6CjwoKCI)`taBx^#}Wu&$-7knn6At52TF8+7b)|gUu zYg!zzVE~lmbFnQgE*CN^u30Aq9F;^=2i86FI*^bO$GiLew70^lJ~|e`_vTp)^}Hr; zUggk8c1ApFv}bF2A!| z*=TF`OoV@u5iD=X?+g&U6kgCR04W;MWjvCGxJ?2}9XQ?8vqALgtJH$Gwwt?@K z23z?skT+J5A8ce>AozT`|31rKDU|Br&TFg%&kLw!32`8ULn)66)@*_$pb(zmFG^7D z1&eFWuiq@uTAP&0qkr<2swrbHNU5+7@@;Gt5q+?xZ$RW^z>g{cAqQKKHmoVI8|}`^ z10oiouQ}g315D}a>grsPItlrrj0ab&+}bmXwM&QyFNybWZGIG-fbfm2K7uabmd~cx zb9({G(3yyGHOKU{^SQZVz<_-n$kJVAL>7CO>?h&uWGcDT;|_(#j_fKaGX1N*OqnHW=$q~X= z!mkLb5{3FbrGy0VSQYo7!f6t~yhB~O!@bBIS{=c77dP{no7#+2VQdBAWHcOrb}0Ozy+Y)k67!zf!5TcfivQh&%6lN{+w5OM zz#L(I$Ozu`f!l6Gk@0&A+0VM)zCIzh1F?fzIj}Mtypk37_D;|n%|$*eAaI!5?j3Rw z2PMW5JULG>De7TE20%hnCiJ<~iQdQY7YEN)q}iiE z=g{@riNyYj2!@{m!o6d>T$IMIAAEVH*MkugUi^;My#|SuA!<_R++F8wzhkb`*8x6s zKFYR^R=W%p805kwEfc^ag&QEy0o#?*jde$C3Yb@;q){CQeTTx>hw#&3?Z}}>1_Oc+ z!46SX`c7g;Q{UYE#pibM7)+mW03(|F_%zgNp~7Q|GtbMDf8WW&V3brE!rSO zBy@|Yi@DU7?NrL%A^UC~*iJPiix?aA;y1XLvKsicW z7Df6Z0sb?shX*!QjH*d`EtFibb=x+{r;M!O;!1ij*vF3Wt#=RtyZbNKqC!D4EcQ7F zb?}J(bzAkaA$r#&Kr0yjeMPU5(y6~^7`T(Gnp(ivE2RN+U~5ou;%OYNOv1|Yc2sP8 zNL8)2s%Qf$MUk4xiW?ijO-M2T3&~vo1&S8l+$VZfhL+I`F!kjk#yc=8rfjQ=DxKg5 zQ&LhwhDg`;UA>=|0DWSrO`%~-SL$9!h(N=s-Xau^Ezd8I5Fp5r+VK!j2*7gt zYxsrQjLWnJ%rytZA3A!$+_mO#q1eW&QiVX1on4 zTXN2~b_UCLlNbp4_l(e%Vds0`h|XU45|{+7)Z^w}eeGG9sN}CL*3@(`=o{D@Oe~x9 z*r8ZVwIOweAN0?l%=(aVqw|0l_0E8Duw67GTz|L5nE5ZxU141f`3cBhWhv3f!PeV* zv4oLCmj=M_>s$dAx3U3ra_igghaSGFsXQ9@Fg&U$Jk?UiN275hq7jV8d3i+HISA%z zt&1g$lTcbm*9ibEX*SBsJO~t>z_XXKPhDgG@B8|~BHo8TyPKoAc*AjblF=Zzpbdw# z-1R5M+d1+Z>`D$Vq!v&-Ss9hu9!|i5*PMuUW37rAG3{}0hT3y9@7%d#U8c;FL&yzn zlT8Yrfqf~g3W0eRLsw#O4$RUIbYJ8($ix2?9{oc^=IICRUA3V1Z3c+#MJ<1>;@I7rQaGreKY(heU1(`z@FC$otq8GqwR)O@gH zA;1&Be|UkQPMhtFa{t34>TXjXF06?+pNkI`$uf?dqPh@5VCIjQCt37B^cs|*Rb)f3 zm2-!#Lmp#0o0ZWb?D`g_qA?^rJ-yP=N3fezM?T|Z_TpCN-68T#U^!k1Qfj1b6a&N1 zdWEqS-Q3+jB1M2B*>M33*N>b3cy)!b+1$3Wo%-(G0|r(I`&7P&cz}>Gthf&Zl!us+ zftwnIJiEcL*`Ws^gQ9heuVE#S)!U7L5(ZDFLqs#G@JB}Q&#GWYPH0k5ZyiR`9{fct zhP zjHD|tqMO>E$b<6zbU?B0c)6N2I(b!m1c8Ojc|a}z-hvln(h$s(45!^qS_p%F*@H|aXX`LSAm&tiLmPCO~ zmI61bVanLHWK*4!cVuk1&lAO>HH1B*o%Z-~wCdQhW5=JDf21&E*J7L%B zTaak)R3=o6TJlgx=022*sxK+HOikfzXLf4>dSxaNmC;{wvOVmcA@bfRg7XkYf!912 zN@=_XgMacgO^gOl%uQ-=w1B{r2qkli<|gSG<#~INCY1cxhw{^P6G&6pyu#!%Hg#If zLf6(@0v}0LX1=J_Mf*jZXY&3nc3iOr4A~p1>=EMPp=JxH`nbD?8OQ{^Fss_2rYJ4XAWUA+jD)CarvG(gr; zt1}0iUZBCe2~T-6{KBOOQn(<_S8@#NMmC~Yc!3a<;l2(YBH|_@TWl-13_0992_div zbCZu9oN}4d$&~t#eQRjBvPvT;O#e5HD!-zXj$X4jIhtHtm``vg{1j1o9<>yV3`&C$ z!OIlr{E1+@I0*%k2eU()f|7e{mdqjPs|TMKlNB7I&tTZrlq z=AL+XM^{5eGrGI1&zO=@2T`l5Mw zbEyc8R!=*>Saux%#rArH;`PmFnA&o=FcsUb2_4qwuqMw_41hn8f#Exvd`q>(`0uRo zjx1`g?(6JA2unp#lk1ECgqQFR9Zav3$bRJorha$ItRG8i>RBtt!)U3tT=n(MMT7i& zlAFmeT>=3@wk*u@<;&I9#9bs^Lt3?r5uk36X{37B!n>TV&MkNC#EID~k4jJR%mrwH zIG8a4JjDx9e3GhZ5NR#epQx=!n?N9HDf!x*Tpjq)Z#Ik}B9+BiyM9oB^EkYa19)P!(joAIWOs_Bk?L>eJESuiZ-V*6#1*E*E z3y-{QKs_{SsqKwt5DPLyVAt2lZ=KB*1D|iq!&tgRH@W~JkJ!JGc<}(^cnSjX%>8LOx+}C2%ACn-`3p$B z@&JzU%Eh6tq(a9D+o{>@>sjs;TrIY*D>EC?P|8M|S2HpElYAE?@9PlZbc2wC!vR2+(#yG2RFP;LMsx|mB|m+Gp8!3wSsot&@rrZF`zP(8PG@vqYm0mMHZ!*^0DIoTy*cQg)*nl3?DQ~J^cQWwYH)vQHt>u^<8F3H2F)w>>N%V~czAO=9DOz`DuvL}*>wnEU<@c`>T$`4^`2#ePe)!m4pkw4rb51mY zac}S5-EMtDu;MMY8C@aHP|zn4_yRD-oVB2Y6p?TbF|o^N5# z;CEK+>2o}EGSHk*L=0+coM6ppPin=Rsb`oHgvxGkM8koq$-3Y1G(YwutND1^&|{WZ z=?Ga*Kxtug;OOvSAuDynp%j5s;nl@&(c|#`zs?<$&G2WlPM@{!csG`#^QNtB!aH_T zCS>WL07#mH_RDHLpwX1RjHT#FMYzLMNbyojZ*R}lJ}d~ z9&A;)z3>OijP1#CC@smVZF*)q}}CdnK`qmZPb6qt;!MQn<$1@;gPz?_Ec zVX(6y3fBphq_F$f+1cWQr_2f|mV*C#kkI)BBhpfIa+%?lzf1`?Zi}nBY5CQW&{JJ( z{lu=&%QOo*{b zoos=2kws4m=cb#-gHXkjt;S8VCKF==(Hf^U^!1lW*B{^W7TLBn{9qon&k?eKbzX)b z;;L;=Q#l~dX#)_rmV8RXh#&W0QPFWEtMf%S8`)kxeSUf|4zrR!!bPWv|3Fl(X;_1CVc;xxzolZdG$ay zDeAFgQ4A|0rZH_ROuCrXAAl^SlC#j_JBY|`2~wQZT8#6Zc!XTTS`7W8xD@^Z*!b_7 zSJB;ECGAY$5vdD7$aIsWrisuASbnb{ZM^19GajP7@_CzI(Nn*KqM~@5JJfMn>M{;I z{)*Xg`8APQ#VO8LndnS~je9Y%VYu?u*=o@-0{0Lh zCf)39L>+6|fOL|Xumxn=&g1OgDzp7v^bu-psR+_u`T;TIv6Sb$V68m%Mwx}j&k?De zX8HG)*WhsR){$cG^bJ`tBYXwd?jgZSDw6~|MDtsP5%BG-#%8aJ7Ki&%-iFbl;29mBf!v;S6b5wTWy*bhrOq2bG&k zytogj0XRUON1CK+@=oMophLCp?rdV?C5Y1r$GDHM6M9u9FnrR50u z(GDax$m?bf0MI9^gUMDqwCL-efyBmrFMv(+m>+OW_3{G#<2+u4D80gC(@+} z?IVDddK)rKSL_H_Kx(C%TCxCEcm=J3-+p4r`7|DzRgp88rI0A;v;o8>Wv2_rf`M90 z8&EmFK>cEh+8Z*O$0lG7L|(kj?IeX~stq0J<@B>J`jG}KmBt!4o*AL?HQfF^Rnu`o zpJ5o%8pZ@wd5bC*1z6`UoY|R$srwjoC!H>I!9DZU@uRO|QwwMCyGss>eenhY7BOT7 zH81^uTqNL{m|Z=5Yyy3PUdT(N-W2{!nuKptT7&vJfe&nekH}mREQ?5l%Hn5Cy*Kv1 zSP{(GJfdig`i5c@_1R7v=)x}(9CPDPCG@wNDZ}TL>ji9;)D1Ik_z zSgrM=HfhSe7BMh?=Eu3F^Z0R~U$t8pjOd{L8P_m|^uNF zKX@z&#*f*}(2%NY*TzAxems>2{sS-)npYAX8Ch7)PV~yBq@g*7fO!C95S*ltFSuiz zaC|EIPAmCV1-q(@3?%72)Pg;XG z4T53;+JD{Zs6T^POQcSkKtYy$9mLm3ZJl)E*2pVzvGrRzV6|K`F)C+7@ses}2_Z0eiZ>wzj$Y47O!LP`?} zy0tBqS&2TL>X+37$05mDND`6{v|>UY$B|eXWw5#dDJkm#fj6UaZ27(r(Jfm*nCjlt z`UFtO?ENU=88;e7O-O_fMeRznKd8uxR3S+$pA))LEYLvrDC_=l7}Z=f0W_f(KWtz- z{5%O8(v&Iny;3YbjJTYPH&F*5foq0KrXGLWI1@}K26gjKbn~nAaoO17YjTj28=?7Z zCJidLHsPN=(Y7n^EkE2QgjgRq;VLvL7!ltKAu@oa3I4sYaU`~2wdai0$pxJDAdCwi*hve57Mf(;T!8Z9`O? zr7nfYppk@VXR)K1LoJb+oZ*3+?;Y}$yY>?#h;;otj78*4Tf(hr>YFg6WDmVs0}f?-01titT98U>?1h!2In49>}EG(-b#mbrGv_#9xDAk-ld|XFg-rt zIw4JiWfZ8=3hO$XqS2|DI?jM$M}IeGodMOrsJWSR`A9jEblxm|oIpIZm`Cr}COu4! z#h@!t`e`7iA&TElfl38n6n4w3uAr(9wf;9q_+Q27vjX+aQ2gIa?R#LivFu(9$8QhB zZAB&osi`J7<6^AC2B-uY(s=>iDnS#l)F#$C+)LM8-|-;M5K&$XOvt>YjAmXjFW@Bi z@0k1B|A)UK;*%zyZL801Tb&|EGLtB+P4JPQpn))|7ZHaKX)@A?7MUf&7^&KvM}t6l z{H~`cr}i^`hom@e*!G&B>Up%pQ(=u}@Tk|Ddg4jBkUhbK`V^Jk(Wxo`YrumhbT7oX z)bO$hSdBF@@I2VY>jQb5t9J7V<%>E#f(|M#EQ`XV!elxcvs_6tw`)P|ZPG&|!AijA zIt}Waziak48?DzB@~!dpNag%jR^mSLwu~P$ZFWuGOx5R*yrf$Bw1M6y3{16Dk9}80 z%6osuz>U}Gh}1Q&E^&G^*3A^^CyT^6UU^xDG7Yk^KyeH)E7M(-HNF-g(GOg9*A5~P zq4o!A^YY<@YH$@Q{72FrNva0_fjbca9nI*gq*9W`cl+2WCaEgNjC7{u!M6nIZsB|= zxAZHN)i1!LsYqsDq!qi@gK`4EILiPGy1QOE0Ef4lvb4b}*RiUA%bd=ibqGF2aSCqL zG|+Be`cTUa3CkA`d8tc27pNT<%^w!8VytH@Y6nZdhC;Qi9EORkpR21>2tQ5*{M>}G z@e-LIIqy{Q1m4-9+t7KYT>k|^U~1la$Sy-dk=1rPr^R7iTnwkX5UKB;QGjgT)a?a0 zjD`S6&ldXDGIu#`0PD#gUx~V%pNX0^u|rh~J@#%`b+#xkCGamj^06EguBa)(Y3Ihi zy|d17E(j|&6f3wAy)R+5&hQJ>(->>~1j8C`0TkWPq>ltl)S_pM%N$a{>)Qd9R2{-y zxVXAeV|sm&kxng@MZLjhH#K<<cv~wU(#F7FW>@pfd)17egqDILLtD8(bem= z9ihjQC+P9yrqV6}%xJ^W7}7wGohg9O`P5IK&&7B*Yt(7L#4zz6d1OP(=unYqaksM&}pcUb`&AjD75K+?6X(ed)45` z@CE^@GF!jCs%hf8kn?&=#mW8jQ4GE1@m`#%DB&YIE*fKc=s4<{81E{JGc76XVch>K z<&PsK0r~lNU+ea98!9UWz@U+{-EPDDwA#l{hve(*pQ?A&52uAHoTv)~er8GQ*hZ=J znN(3<7+R1kF5fk$sn(t76-W8)8%hmj#%D3U9%G z%XTqpZmj3GzA$*h?B!;$o?s@8aW||rFujbwW$$Cg-!8=v9^)_nAG_>cc4f>Qc`HpB zRHh&b$fC}IHARwOW+0p>5KZ$9161mIBE@is?15S3ca;B88zqimWCl`9**bk(4 zQplFpICiv!k)N?*_hItL?PqO(l!RvDHQ~t1;SF0O);0-xLp{A{|E|i%Rvc}09XD-p zU@S`yCRBuUKmfsSh}KhBBtca{)!lu)_8=-Ss|eR<@k4yxoG%jn;8NI{DuB|>Kmm&6 z7(JDWm9#NCXk^qj1OBd9e&p@>^3ig9dND9VyUvZ6uU~kNu|1yz(YD!$)*pOxMTeBy zGF6}TXov_!44U$~9k;&-t#4!}5jEle$nU1SW}J&B9#l2UfFCCcF?V-f8yqb94fj@Vv4;L>-%PGZoMe?5XIcw zLMQ0pN_~U^D;29QeN@y$EJfdx#(YUp6Wtf4pHe%{)%x1}tBP_e|RG4PP z`^;5sm&PJ&r~*yc^)G%LncFh7`&Y3MYvR^iw5Q?&8f>VWJQ}@Mf~v#`Aj4>WUy6)H?x!Ub8*+^+H1Pzh_S9QWU!D${*h^ z+--rLJhViY0cIg)j z30=^U>W)B*1s+hN8=8nOJFMoskeev&^Gaj`eZ0{k1rK48;P!HG)kXZSqt+%4@~{GI zjc}NjwLN=@)X&(L5d?qq?rXuuLD-wpXy^yw5R>mJ5qHl8v`X}AJaPr;4!wp*IVDgv zMP#(aJ3kotT-SQEd`%DHEMl;hTG3(Z|BHY8VfcaY-F-b_loFRZtZZUkXA@gdQ5^cxTVqV&~{KrHO~vdoY%OGq(^mVr$ZS?{4xGUswGW zEn`38y(A|OHhCHWSJIG-D2Xauix-RfjHJKiI?m-&wB5%Z;7tR5XZXl%3N=FRy}CnL zNn>d5>l$jGM}DkF>Ei0twvhEbPnI)_oR7#d+X6=+_NOLFLCNnD zYbnD|m>#`O>~44_0n9lO(V4u_nZt)eCEJBFYK~_N!|q#S9*DN)=#V^vhU3vX45^kH zjuvE%ZX}<&FzRD zrO>g*(bJm;EPWBo(gpV#WAMQU-48z%9&G+LtRv%Uco+33u(=0Oik>lM&G2dd%V9b^ zdn=+{)}qUX7s%r;kv8shOa0ryhdUu#_*K{@aOj;F{Dx$f-k@*P~brplqgJ`B0Mxqfl&x4SAgtY!f48lOS zBWIAaO*@Ve$5i{SC1@JXmt{;9D3*PB*n#s-S^ryvpC5-?QSXp-LA+L(B7NYI8#N|e zG!H^Mq^l=Nl$9y=y+zV0@OqL(44i;HNv6(+=_q0Wpm^&el?F9Y__bDJ0HKLGvc2T4 z5^>WYm_hv=R&Xwb+XHq>#V!~f?%8th6+y9q>N5-ib#D{%k)P=Xz|uy+&i!GR?)9Vq z#xa7?_A_O~3Qzpmp&#JQZj`4#P88stN;LP|X&g-=thWpuu$r{|xzlUn#@ojV*JbjvXl z9IH!#IQyyya{j7YA)#*1fb?|Kk374Ud1e1aM;aUG>jXdYs=pYYXH~PCwSWd_(nxJZ z2{o)6qE)1D+3uOL@8b6Wzg^9xE(vluz^py)Oz?r-3Rn}bB&=2r&~}#m;LCtH73<7O zZ~o6`e^IkSP_EN~#6sqFm@f7fdsqa0wnfaT7dGOhYz_(ljIrYUApoqE+Gc!E)0xrf z#`5@`g2hO^l-S}v)Nk0cvc_TJj0q&g7(yQJz!FrYG+;3?CmWq98tgJ`{>6y0@9MUX zle~Qq(YHm^%0I)7a)`}YLwsDV!+<-kSo;iDT)UI`_S5(Dej>)U%X(i$-*}#ag-P=F zReP6yqx_W5VT#gruB)Ecyv@u{NW9l?5xd}Do#7Jn;gE^Bipb2k?LB5Xmy)*cDg5Dw zJ9D_Yz5i%i`m6uaLY8DyBY#jvy_3o8TWc(4I|Y^G8Ep(0>a4cN;Bg&n#ReDtoi%^g zB@`GmdrkpYT_|o|i*8!oo^;FzF8mB}qAz_s5tb0KvCvjZdEq|MhMsYsURw&?xOZ-umYc`@k#=hcx%~^}VmotqgLf*+Bu10c9mn z`SJ_pF}9>zA=nPN%@Hg)mjtkN zr|WPzam_5;mthQ0h~{Jv&An;`f?Rx((aoxu2zow`c({{16c+fntj9*i^8VVFH%U#hv7AKnUgerzV$G-ow=?uY9O~T(VIL zSp_BV9wGRLvk$%HdY;^miYh+E=mJpmrMt0vrn>Tfu=nP1J?H(u@b{W)%z|OMrfkWQ zF%sE@_8FzizEn!3kffAkY0-MkT#+cokiAkQ6^TUJY=uHX(xNCWBuP>!b)L_U<#8V0 zPtNZ?k8|$d{kzY}AHz`JKFj<4dcT(EBKJOcEBoGTya7g2M&0Y1Z#57S>*}_9YwX{@ z-~X-byPGyzf8`I5eg{5lA;zry04>0N-zu!1hfsrBMc3);YBawCNv(pDLV;%%25+(? zo}%lHNTsK~wuP9fK43#8If^n6rAg2m3|qYBQkUi?qvPN4y$S#tq}zJy*40n*_FA`Y z9m9Sc=0^JXSFfpNW};6~`#V1+px;Dg4!J$vC?6a1d+z=FvZO)?7{pr>^K`AO%G|wH zTUlA*6i@RbW|s5Wm3DT@eIL&d5W90dN`G9aY}8RL&wXyWoOpn`c&woYv^s_aaZ65W4(Y zZ{xOGzp9YBK8ZwOEA%sudqC1+8!2aXnDD15nHxn~7b%Br_;HozrRbn1(M65xyvO6H zIFAKW2H@At#{uH?G63u4USEI*3n1b1a_k=Z8r}KceMrxeTnbQT`@!o1$lQi7Qy}+&!@+5Us8i*})1VFrZpH zwzydIs+SlH_UW?Tj-)KG`3BJkg$?cd4<1y68#&;EE+~doB{7GjEr}pV7}DNa`4Dj_ zt2s*ifTqn({8qC5Yos!cFOWU6+@UE(DTdlx(x84VAmK1ym{c`@cvZx{tN5qB)O)c4 z^f3o}*4aXonxA@xjjwbEFK_bG9aBqpOzm+aLX=xt}YXK zyH%^KP!i8KHtr3ey?}YTxmCObVa4FnUJvk925VDvDWJHu&6S{hpp4x08z0)Axh1p> z(3_oH75c|AO=I>w7mrv}#uMp^ zgOTxeAQc7`W6UfdvWe9|ebqyLov6US_)BzD(cdMR<;YM;qs?H{r-nM&cbFVlPT!dv z9sOzmB#PFWZ+v!?h0`@qQ-12!d(_CrRicc3*CA}(XA>q(x`x`VrjM3~Yb)Rf0TmS$ zJ1<2;6L^usbvc=#f0*35bxVjocMeZ2Q!i6;ah$y>2_N?;#aSL~!VU%HciUfKaWJM~V zfeuizrZ-#BW5K&~#e8<_%ly}FDnm>f8X7<-(!YwCWH>Ms@-4aIh4(XJU#TYqb+$vp zw6r867RSl+=g%iog^d6Y=$L5p=y7=yLpmf@r=T*$R{#D32kHmMT&|3;-S7R9LRJ)u z1%L9bX7W(!cXTl#oGYl{9!^>XqNr~)87E@PS|cnlfIAN+b1BH*ZrAFf{rng( z0H;Mq>7|g*r6OMGJ2*IKx$Z9fvv8+-8)ogjG1_sj9WCAg-$N;0xqxL=Yqm<6apD<4 zVtC1=-^r8+J9%<-xTZTcrGVK*S5OAN70cV2ePv=UJNSyvE#-a4R^!OY42HB8I zD#@BV=Ua1jlJTw9tA9Bcj>R#Zj99estPg-r)R3&y9s5QOHL7VK|1G~=f z2+4F+jlv)Xjj~zfqRomfMwWAq|BHfneQ<%0{ALX5K&I>TLQv-yzfL|eMs#xN2p_rtBet2rX(%CBZULZg zf-mmi{(j^AAuYrdD!Lx*?4YHyZB@=aecJ=@+y3iB&tu#WK25YZ@iafkC}9*x3?H86 zTT*-pshhr2_Rz5DckMd3UseTo5wPpEkJ;{TcO$;hI&;_BS<0`YE#E7XbS{$)p8*Vv z7Y0rj{q%jpw}VWd_k5%NY%EhaS?TSec08XgUXM<6DmkEb)F zwC0u3ZKxra#hkgVoEJ~6mp;hH7wGC@J;+{uo`2?;I4_u2$K2k~%uIgsKrdGuBE1iw zARzXo2`Oes^T~nltMr|q?{L+Z?*~>54`fRjs^U{V^~;uUeF>te?!1yBgEIqP1BntURxwHqTq}b@UiRqduo;ZBYv3 zxn=G6uC;sqO0vw#?iO-}B^W@7{So)v8b^ze1*f(gS4L4c(mt^~Xe=(T51`)BkBzPt z=0so4?izbJd*!N{m5z;k)Sg*I1h%PSlAX6W!#~1%q2P$O-z^K977o6?B zR8RN7lcTTZn72Y<7?s6J4SF3rsputs8P~dSRflG&Uo{*>QX0_`e9=^?|uKT$L2Y_tx+OA9705?qnd z90brj>YGQ$fDf!gXL`)0o&yK44C9=e*^je@=MeeL$Y7~ec|R}j z8Z8nKu=>f%4^a$rZ+tl)0floY_+v#zM{h-rwf44Xg81-fZ2TB~L~DCs3*#^k-QzBv z0HjA^@rZw^b!av;4iQoGwg0(QQipa&!~Z7Q-56JNUhMx={rSKAWbM$A2|bE5@9yhZDCk=X~Du!baO{lb&d0+0R0>+`KEoHmCOJ?ODn2UyaS2Fu8Byzm60C+omX?uDWb2`X@G%M1LtX2-FUu4eIBIqq@oi!OXTTZ^MnEo%^?C3L{V`W6o=`77_>@!6Y^X5<-moXe(JVA!UhTUDKHT`Z!~H-YNxi$L+6swt#eerEDD)-7IEDzKx8JnfNFJq zD-|v&Z<{8UamsX4+zi|5+AL4eYF*!pyMfS&^W{NigzU)347Hl?J2ocB*z7^$p|$4H zw<$S9r>(;Zz+O^Anw01HW1_}v%6*U<^h5LNAkopSV}pc!z3bFX-36x~s>=^-zOGL# zrsh5m-knUazwVMH9`>*AH4#mIG@vsM4&oJ0s1?qZal+N>JPFao9JhoC#6Aph9YO9n)88L$+Hlb zj}ImPTN-`y>URD-yiqj7k#xly6g}=8$A!h)lgdQEen@UDV|2Q2pSyM?{HhKmEl6TetRTaVI51CQYg}I;#TX7hG zcbN`bp1zP|%bI2(%1ut$@}xK$5fMQgRvD$uF^3w`Tt8uUNrPq{sy`AQ2=N!1FMy-S zaO_O<(fq}WQ)Cv3qN{xYrv{%E-{D?^oRXUzNv ziYtX^VgP-x7Hh6UgVzaqSLx{Qwr60S25ny~r-k7j;PmfD5U~c-^4lb@xzdl@XZCa> zlmYR_s0Kh0n6|>ETqyVagF=j)*^zVVqLAkA$BhyT4l57S48A-958DzGZ4!#cAsU~D zx?V}cc+rAT9m*2X6pb-jS2@U~ffg>nn!^!2oxrinU{r;?&ciBs+M3gYjZ79c4qEHG zl_!{^{}~4;NIb2e{9f~ZspH5b|>7=3`Vuv%{4hoLF`RGp!zbkhZ5A|q*VWxGG20O=r+r_Zaa z$Crps)*VB!7Ze{MBs5<)T)G7A2NXgk$I!Y^6;h6L=JlF86Z4AESCmDs_5~NuGg5l% z&%}Dxtm-?;tx_IywnI-u7B)UFkS@zT!wlngJCG=Cm9$FO45}=eQ)@9ozXl%tf6!wL z@lI7539ulJK@7D=ltlKTxPT?A$IeFfh1LPhD6v7;!KR%|3A8z<3MM zbZdtSpLGKU0#BQ{Z_&9Vj^(KvI_nuTMW6kln+7oZ9t}&3JaE|$9t0&Mh$4|8TT6-# zXb_;un6n7nTQnnrZ0E0nbnx(Z0ObO=!~B1ois*{2ZxWy&n2oh&C!T^ABeD8uSLp9s znBqVT6^6D?UF0a$opnmoM;WA!ji=WSh^GS~i}82~H17XA+K2 zYWPs)mR8gi=VVuB)-*$&TLDM)`)MVj&%Kkw{aQnfI3PdO6(MkbBcw76tOu%KQUr$J zQGwITR+GEeOHefhcpo{?WM{Y{9Qj{1Q)%S)Q?j$MRg^^g%>I>qpZH@Z5(%@CiiU|2qotuy$Bz#O608>v zo~-u8^g;vNn4)e~2yeQ&XtOJNpMa83)jr>BCAqZB8f+X^JL%Jh9g45B6ip&R|6oz% zOJQ9~$Pk%q+U5A#c89)~mE&=qa2$=K(Eg9$JxOzqyUTewV;Yr#IHlHmb36J&T>J4OX4tP7~sSsBeqUc9-wKKY$EWoRfEgs+K!ag01=Wl~!<1F@P@(E;vVV5%!2R8iJ!)1FrY=_a( z^Uq6s^ybDJ>YPi;z4c*@R+e!#;C*lh8qsH%bN=;3n@)OZX`0RiA} zGWQ_F1`7AfHAP*9C4V6Rocb4_&4ZmqC-x~B!`<$ppC*wU`eGAB^u_hkA;1alEZq<>$k3*H2mfl!B1rV>PU3xnn9O$m zY@63|%(h^Zd_i@p=&N?4UZ~ok`mL$xw z&U`efaa)l$C{K+|+3l^_kx@~0c`l-`ROQDeYb7KySMF(11-OWOF6sv_lucCLW=JXR z9-43Nkfv#cj|xB^^0k6yIMO#*RvtvRIkrHuq`P~;xClu^z|!vfxH z5|*^8)$IC~T%gb0Zx_;Z+6-4>Vrs55C?_yRLdCkPr{ z<7^9aSdz5Qc3qA91X=SIGPWrVHeYNcE1;ANlcA_Rgvkp)BO#doB2z#vt`-p*nosRhTS;&a8Nf4fxIOU9{=v|Zp z4`>*}s+Z~-XZAt0@7z0kcFa*{Os=0T`kY0aYCm9N0y*T{ z?ZI0uYXD|oB#CglA{ zY_Nv>Nig;qH&WRdjE$>64x1htz38++?ix2zTpk3nJRk~`X5-QIq)q+%`=2sTMVc5H z8TE%U2O%BVR3Z7|cXQ4eRw9fEsCrP|*YGlGWs^4_F!$*3otU*%^Z-r^Kos<^VxBgm zO|lbJqTK4~KdjcVZ3I|t zW^ms9`z%{gyEu5g1q!+&0KV|$rclGVQBSxViej^ppoF*LO7tsU#?a{tUbmtfasw!u z2j`BScrUue-H1_<|3nHj>Yb*5Gl6c^q6obcKtDy_%EokJcL)6gd-e}(qfaFGf2c15 z{OD#>2J?*3c`=IA7At7A$I#*S8WFVjBLe_Me|&R3zzh4I?7T}fFnBggJ`78C{@k36 zQV{A@iQB40*ITfMYk|`63vCh~-lt+Xa!_lNh9HcbCj(oPc?-88ryY*ww1h`}w=;`dUHW9dHApo3587Yw^2bV?dY1Rb7s$;Z4S41c~8-D z($OaxDxUQfwbA|jREJP%hpjSlc!4%O|2!(X6a|%AUiQsLwNybvU6?_@98g?#a%sw~ zE%2Nd#LW7Rd%9R#G{0G5Fy8=GR}IH!L=~kIHw%1Km&mnGju)YqLjNV|;v9tYqyX)Q zRLwFPXaZxRhaPM=_)0Y7_ZHZod4?_+e_@d@&)ajBwP+R48RMFUhix20C zF=h6=dpn9s%Tu;d0^WsHw-Z3Fomv1VfQ)$$VVSn|HzU01Xur}v57ZN9Jc@qhl~_;9lb4Xig6gMZLc54A zE3xLZRvD@mCQujG)_VQt_FdgkN`4bld<*1(KC8IDvClN=%n*6}_>Gqx3;@2va=Vw0 zqf`(I!?8L7IgN$v3DOw8i7Va`)Oh_-8n{nRZOxU}X!(O5_b|gl)V=eW?T?hr))Nqy zK$eT_4G@e9z4Eh4o8)rTR-7GdtoyVUN*L^a&d;;-C9Q+^hg|F^tQwH2+YXHa0QQ(A{Z<$`G&$zo*ebf5B+3uO zfy2{LRASRE4Z+r;!dG;i2YLYb{}uUHgv5QU`-Sa_*NiO6Ii-ZiGYTPcct^Y(xm9>? z;TZ7tRS_lh57*;y9I`;>+zO!6Hu|hkF6oCKerV3VJK<5NJ9>$~a9YE6$7TpR0T^a8 z6BGvG5C~;@y}em{sr@JR3cUvzoyD_)HKtHKu<7A7En!wjiMo$8fbi&7NtC{3x(hVg z00dbpx<@i8ektyjDXC*88C(D>g#i^Zs5V!T4i|K{D3{%IV#eYrV7+jhG`S}J9%CM1 zglm(>Nne}JW)f$VF%|jX1Vy!}VBi=R=0yA5jf?g^cA_?Z+fq$X1jnu}I^UErDK+lb zMp69`sf!cx7~dW<`BpgOjO=QtcXgwcF+xnkVwJOUSCG`R_$-E?=?6(>gf<&OI|`?JXJDGk38zhcFYrfLVlIfJ?Z z>2FTFwUP%&Ne_+YU>-}!M*1<3;DrubWi02vpq%8S%+QRs{<<0n(CvuQTJ)w~ef6B^ zPOBcWM@fX=XsFc$sg@b5WK*?11if$ySPPg;4#<#`ReMguVj{hgQ&*xw1+E5IDJP!3 zU#DHEXd+ZjlrbFb`D~_qrLSl=s+|{bH*>Q!>SrCq+mx40qvL5Qt1YJM)OHt!e-L$Q zSM`#U!6;ZEsV2dUV_{M&iK!EQDh*jf_PH<2s>FeV!KVeeinR?JL_bpXduBW2C(A-; zV~byD6Ewn_n+%b@QwZJ(Hz`;XR?dCSVZ$Q*83<8M?aB4-LVfE(52+2!sIS zpR6r~`9~$Z*aB$e^B)od=)2Yj4s^tFdqbTcmN{`4dqm#vU`oSU?g-H872Yr-ZC{6i zhl77IeB(X6IMcqpsXRhxzl!bl|)IW06f$#2YrZkDQX+r|Xe$;=* z`(@t3^YET{uxQWs@QmuI0QhN)9MPXC6XhuqVaj+H%l5w;c7nvHkI>L}Pf9tU z&fECE*kfk|3P#h#Z?)!SSc@Lg2jVusN;lfg`&>3fPRc12Un`ZwD3dG@w~21+sL1Tg z>aGSrhN0Q0X=|~-`^MVVP>Q<#4306I4hl_o27-Q|fEXGZ>woluV9_E2A%K9a(P_!{ zg&b2O$+kT^LNP2VN*TwnL4t$m(if4*!6&v1858H6Zlly1D3(HHlAA8597m)A;?zqI z*6IT<_ruis4aT=$;@Uhw)oA&~eblCp-I5;Kt*^*{0{5pvG}hH<9R&MCEa5=>Df39iJ7JiB>|93@mU??$JREv zv4H|z^K@#m;j)}i)La8hXWpCEplk$(9vZJM(wJz{Wmq+WJtS#Tw)lx1PQHj~`0msxSh;+r};$y;oBT-RHV@MXxxw=eq2q&r3Vom*xI zAg%A=s2>s^G(I|bxgMRyYiKI;WqH<;Dnf7Sd`tLXKU%qA17Cymz)#d5h+6xgcYMK) z==+*$yK0xfUvwFEMI7K&UuI%+fGRl8p+vZG9hjjVKvZ>=D^_o8rs0n_tX!>-j|h^bzgXs~vDiSVUfU9hwruIf@NiV`Mfq#wDy=b8TY@X2WIdlF~k$xlh1rk&Kt* zfFkQMHF|H>dbhiyk>KrjrHA@R47v-qElv4rL}^Wx5vqOal{f+Xl8y;2cXPRiC{p?K zILy9&W(^hfsU99Q+HwH(1_kysq@k~Zan*G4%RbXKp3N_c44;hB^V>?$*g2iMZe@8vj2HY z2sLA?7Ry;a~bX`_iv z{u)Db80Z}BXvoN~-%ih5^n~S+J8Yvgy`mx}rS#GB`)SVP!N1Jf`y&#Tny~|=KY7Vt z;Td18H)XvCd%SCi@;?r2d3ddqeQEOM_eV63n7-3xy_}Z|*oADfPv*FpWM2r3sR4DIItMOAAH00Z==g|ZCk8!$aP>gybCW-Vj`I9KsHf?p60V}K* zw|lpG_Po-gtN+i(zZ-S*{4Rfa&$Bx~r_KJQw-5e$XkCfTZ8wdR)#kU0LaunG1@0+H zy>{~Cuul)fdMbK<%E)+}9xSp+hZSRg9LElFDgH|RlkDc2d9S$A=K-e}L`Rl_`h9!a zBT?rp^dIQQ8c~y%G&t%$6)4V=lhLy>zBOcFx`n*mh)p$ss_i@J;^i4~R(g9226Ph2*>g?i0yK)kZ-P32x!3&sfb8Z7IaxW(aMGP5C0`e2h} zE^sLRyW})r<-@LG5>lw>arW>JR>lDY;u4+>@s9Xsax!sPbP;*TJo@7>Q+a7rwMc&2 zDfa0nqsF4lebvReqVDayVgXe`hj=z4QL)Kel%W4s78xEWFxqhdZttUNZ-Ng$S~VU> zJHfb*uFI%0d(%DxjhZ8m-^+Wi-nQfsHj;3ja~u)aeDTx6`24%|$zRZ9J^BVRQ`azZ zP*wJ7AUMGZ*qaQFy6|m|LL8&6=c6VgU0q;Vmf_(AaQzEys#8mTWiTh!BEm*hYtC#jPUbcgWd7J3Mb7Mc6^J+;4<6y<{(9xSSTC!T8^`WHIsan!-%nQc%NHNT7{8%J}lQ1UCo zb5zL>1R_hBT@nrpyG=XQuaPAi-p|}!_M(=HP9ifyx~V;QZfa!@03*pU^0a@V(8p6V z9?pbX(*NRwqRB0}0#Vm)o-N;RJVrd{VNGN9?}+`^V(EUz2R*K210kdcY{(n>om-6h zm0~WdF(YP!F|s%Ok*qxLwQZdW-~?e!$OqN{`GNksn4S4a@sJ#e@uyf-&6_=$2~ zulL@+OosrlA-DJif&EKa`To5`J7~E+r!uUswtk#h0aO-9>&*D)X$c_tJ%pqlq$_*n zA^V4qW_i2_fIdi`m#`77IE>gA=56jdF!;*;%c@0p&o`n=f5S=yh1FRhJbC)=2q68Jfte6P zeB6LE^FOHD?$Yte!W&*M<1Owcz|tt{af3?2L&g7~a8spf0U&$S4n?|MdH#hql2O4v z4m<56CeN7r;DlUf{`qkD6cOfa6do&bxRN*P>VJGdR33UHx2zU)1A1!~5e=-iWjemESdTCuKBbn!d7@S}c!)hDen07yPx+~?3c8lG_8 zh3$o0GhJBL=ohZ+x-Qq8L&0?qS zQLWb`7K0qp{XG5dGW~G)v0cTeZDt3hn>l+H(P#{F?l37mqTj^rb8}Hw4c%k@43*C{tJ{hM+D z^^mCfWw1PIe*Jgg;?+SDC7pj;>sjkwMoUUEJP@SX6GDuU*K*3q@f9U9nurbsv{Tzm z!~2TOFqm#k3-CuZnEH1u@!{MYeGAx>V3HueqK60;k(`l((q3t;`m=s2T& zl4uo%&f+6=?{Daoer=_TdoT4`y|?(bi4k%CVBrVuiA`yR_p_C;66k`F}|hf z(>H$py&T}ANHtQNN&e>~Yaq0U&Hf!-;J-9QLB2S=u8rdhR+*S9@&(oG$QC{DJAIkd zt%9zqpiV_P8*5X*gW~&M*{B}t*FG(lno5{HGd*TMnk1a;?cyV~mtFk;=)Q7@%l@30v1^<<;xgt zkJ#?N0eF5X?Bf>yB3?k(0YaU3men5sQdRH~5+9?=Vunk5r+_sQy(hdbSjR$$OLhmSjqh1 zpHS>marK1oM+Y;5B~$s@pTp9lSTt>yto#E@=T8C}LF>qEz_KhqC5m=F6+%{whLc?K zC~;)jq0P4iiOs0ovlC{Q0V|Zb^as(c+!D$Wuto__i75GtgmR=kBYTZ9-V-wzMd z0|N74*PEGpqmV6yW|q^OXwg-(m@9CrJk%Rls#PD+VX2svrh`EnR3=(hKFWA8#@TWiCRYJy z1orPOIv))o@)4@cHVz{e$tu8p`Zdk{IvPM7`gWpz)hvA;9UBO|Yk9IrcRuvB&@@3K zR^T`SAC;rX_B#i>f;l?{YH5;c4muSlzMw>AxrsE;b;^$;GrPD6U!627B(KjUOvWux zmU#z-k9*A*TVMT&+;yNWT-yX5h8&yhE;u#dXako>#Y0c+?#Qr--+kKLtkPuY*7T z<)=UBe9v#J?CIS!wSba;QVsH~=yV<#)%3#@?L3Qft9sZ4K^YNzk~8AoE{$y$Te zsuk-*t7g43-wREmvC>cmHY2pFJ=H2-VWlNLjcvde-*qBk**wQdD{~ixEjTp%z9^=@ zTK$D&Y{3EK_6bL93!YqMacZ(C-Hu5}mp`N*TTrVC*RFZC<5 zjEk}ozyRgw7O8)eNbbaOPt~IwI2`j*6nS*6McwF#yOWDUMf6cVv#?f0fBT<{t@@u( zWBq^UVh!2VRb3B4-&4)Olh7gI*g4ro^jLO5V|Jc1eLT>rsO`IN<~>94BaLK}GatcH zEc7hd<%17M0EplHw}`LPq(9%s4k^j56LwvHGctQ2dR4+1m;Vo6(RmR$Yoc3E>Cci# zLNDAG_6?b2P+Jccy=9#(Tw^wEt<7#?HcRNgnM;X&>1;qDD*D_16R2KMs%VFouR!)z zM@}APH#n$DhDB|heg0{-1lS2cgUZkE=jqEpYMBx%iWXHK2;z#R7;TvB1M8*SL$`$ohr;BXo?CAgf`;t|%*Ysv%Nel#?kWO!W z!N}|PSgSx-ZfeU(zYZNYup6T8vi=2Zb(Mgyg3N6%Cp6prQ;UxSKAnyl)pShMM2x*? zORZLQPjoH2DAXRskv%mBh}wp2al~+)vr8XG%{vn1_H*^2cg)XZa-O?k{uV%0*hij| zr7y;34s*VKC{4CNTRVfG1%lyZQVYhc&DEHf+voW6??_%jRxPWM1mu$Xiz(8dAt|55 zGK<(hs`wF%@%t0B@y5I6x)#fjQ$hgUJx%#@=zR9nd^3+PP2sI5{q$MOJT4hoQ#wK8 zltR?iHF#`y<&{wxa@_EUh+n?x(Z3HHoc3w&->S3~C2c?seb&tx{RHRh<3-n%Ndirs zP>_IaUZT8KE@#}P2FS8c67>FQ*NRSE0QD!(Oz4Yp*Lm~Hz7QVmK0xDqdc8adtBV!h zb#R@AJX{vAK|cVk_*k=lpHwv694n-#-w!+ZBSxLKmgk`d)(hA=w|m&$=$LaV`b)Mb zv$$?B8^LYHGq&su-bBr7wPWLj;ulcx+qtM!S#cTC_u1N$Ui$kHFiN?+GooIW#7!80 zP%ndsbGUBws*BDDxdw6)tS|%c_LiqQ;F+zee?jqu4%iWIS#4|pF#K#&%2+Q{)NG4= z45fP<;Bddez;)G?AE>PS#qKhFiO_X=AiKwIz`o}wpv=)Vq{e)5g`VzJy6@n~ztBN3 zW2lD3tauB4p6G%0mdTO*sAWOTD(1HumH2*?u$Hgh0d}&Z?jO>x+D9OUJR{`bfDPR# z$C=+v0K4ooiz6$Lcc;XNcF=W!+(VFRM95!1+rD!{1{2VT%-L<0oi&OG6)(r4L|Wff zNdL5tKr>-V^2%s`1PW7+r$YwbaJ>Hw22G1zT@Wjo&3h(PbFOuzzvq4XL%&@h^lU%_ z^zjC<24Co1oT75J&=l?x+Y?hC;?2-{zElk-X0d452e?^OZ0u;)6zp@N$j~=f^lBp@ z{9m4OV1D|zf%G+e_^J^;xxzF>e48_cGWXq^;jSqMzcQ(>xV5UzUASH1$ftZ!N&*B- zp|GX>_2!8u*#G_@SsSk=4_me#TSY(Vv>zst94`d)Ah%AkX(ZuwRm?=NMh3*in z4f>*$2xjk&?-JdX*g?pLs^J>HoE?MkshUlJ)$V&lKXJL>UqVn~fkqN~hvUufnqodJ zcqenjvA+$nL>Qj6)TViNAe8k%XBp$kVWdyWDLkcpg*8WV|8<971$vVvl*e3w~2Nl+j@(Zv2Y_M z!w#)q@Nr^?SknYvT^}%FtRvjn%fo>gK=6;s+X=)a9&|0;vTwC0O}NgkL>xz+1A!fc zR=~d_`VBGRW-28oe}%hX%?^E){lO)XWB}RDbD`XIKW)Qq?F-R(7Azz-gC#|d!;&}#6AQ@*6G(v@{R*VGfx-OoW z5!;U#S;712C7PnQwFgPxh|Vwr;migvHn~GOY}O$)!S#(J zTFQRV&=>mi_VvZoO}(F+E3{#e4%Ri5ZNCPp(GW$`t0%j@A0U&H=H0$m!)+~*2ZjSh zkeARGNF&*!I87aedGl+uMB2uUji81W!z} zsjt2jau3K@!EX-|9kq9|MP>NBm&`_*c=6?1M`;(YuP{%&;3^45?=z7v8crS5Of zuc*8gvM0h8!}wt&4)qzkOm}(*=5lYl+S#on!1|o~4j2wJ7##d2ST+X^Z=6y2-?GxVtisJ zy)J!jLn1qLADKv?3*Kmga(-VKDh>u~~>{YLITu!ELda%`^k5B#|buX>A)p`uU|8x08HK z7F&F7IyqU$8QkWIuIBo`8IEY_N=GHge&JgtphY(sfJZB6C3W;I+n9t>qK@52vZ5WSV6x<66PkfZ?#n?f-9uQRiaUAmUGeeXBj5{| zcbdb*$t`;ezrKfBRj4b>Dbr)6ADd4U+vmv$tcoTKGvG@MKc_-a9qI1?SU!|ZL#=#H zJn&-188yx7>uNl)00Lm^D@R)-Plg4g9DrnUVl|nl{kW^RenSibo**^NL!w7PM>;m1 zQDOZ7^f-xnkCeS~&yNOt8HA&8g`(XpoGr^}P&NC{<9`0dypf;zZxdGWm_#`5|*Oc6yRn< zwrkvlXOm*-y^yx`q`|1bA6WG+~O3}tz&xt}VMc6qY&OztePpSVUHENX8l}o=dHa2!U zj~IwhF2J*+<1VHFy@*8H6CzS23tbmQ*g_Bm@CoNC^BWTA*HE1|$v0Lp>{;b#sG<`I z!A}3Acd2+3gibJVz?I_X_ojnX7z|q!T5m*U!#ZLa)cNahAy*;cWd&R)10dP0)Ci}d zyEdm|jiTvTC}k?23ks6ya>iH5!tUi1h4d1r6|ndyAV9z@o#qNtETIid%__X|tr@XQ zCg^1XjjbfzkeJt+0M-bwY^o<_P$&}KG7hQdP{0bX3vK}dS(l;8%UUyBdKJu@#WM5t z8j26I#))`kq?@%e7kVGd;Bg=@p=ukz1{zDlqOiJX+Oe@c@^JZIvuC`rRW70WA&*Kz zn+44boShaE$P@#*1k`RiF*dCZ_1$g7_Xb@I?b^Q5PsEUN2~o2TP9BTw(s<18a&;j} zci4Bp&N|WdVv)@uIk?xvB8YHMNAxi#OGn$3#ID~ zLBA;jHp=6Y_igEA9xBUQUWl@SIRzcTun_~awQ+jX9iQu|jAVU(TM=adsOg3uO~xs0 zq5zz>ZvIpGGW~cs1V+%?^|XzGZ3s={M#122XHnW2AQgcM2DGeL)C-B^KqR*sF_!yF z5Dx`7ky31e-bdY5OW@rJP~z^D4X~t=AaR`>Z=GqIe5lySc(EMY3kTX#gJuA4bab@m zaHM;NcPn=#v4L_9iXY;Z>^y5W+?HXAxE&+n44CW<30w1ZHT{AGU1EZy-Y5Q67fob1 zvO_CS@b`IbnlT3W)5$oCCnVjAHYZHy?aoAG?T*5HGbnsHf<65LQX`iQSgu2VPpsZ5)%$eT^!zQ`ku7ba-@3S(nc%}1N+`7 zCFq8f8HBtu1hPJ6i1wUgYw%32p+m7#EI<^sL()rUJU1JOk^=S)vCwXW*I7VD1ttkw zU|MDBiB#*O$5mME6f!Po+=*k`;l5^19)W;dGfUJfX@po_IyAu3;tWPyM&VF4)AHSd zOJ^WeH>ylDS2V=H&gTcFL?q@!TnC1!E}Ft8lw3IX$T`cyaVSMMNM6z_IYToJh{N^+ zHtSj81s9UD=1p9>AZ)Iq#8_3Z_;de8t{sU!!w-g!(xE?53eSJW$D{=K-8U?GBqm=vp!H zKI>+I@Cy^+7dvcrM|1yjVEnrcsbI;Knzc8?E>F2o1`H)s^Zd&T)yt^D1iE0Uc3m_Z z5Ll4^Ag=IsH}!MWUqn%v)96vN%5p299zJ#p2`j|t(R5+#g4NLrAy#jkQ;)na=xJijqed8! zjl8k0ejFz5)DG?%{DXqp1pH;PxVJN}hIp(yaL0KaeCN0Nc}9s2)!S%qP8xSkBMDDK z-QJ>F*&3{kU`ZnSHRM=}c2?RTd7iU%_Xy)&8cFb)r}tdn(b65C(1Kh5*+Mb;2+ED# zRT!0n0g8XHBO-qs;1Ii>=94mkz%1vP4f>VgP8*vVHbZRhMaXHi+&`uzhoicK1;(ir zoTH-LjKa}u>aAIVC^hq4>ys;+duxVZ+A=*P#Fs{Pg!}$(gScVa3UpMKvxrsHIuIwc zJqEBwv3B+7i1*UOYJG%{di&Dy`@hAY*c}hYOY>JRFlb%Mf>(TtKJAeLNP-MSVJ5ii zA$WdUgKLr64E1co+XaUak#Y31`k4->qMcw&E2}}})KWaaEb;DXr3#QUq>A=6em!F( zy80Mp=QPImj@)>zAj#XkB{Lsqb3u3W+y@UbN2kU% zmh3%LzJdgVEX%gjkMnm7o>~ejZwI#$4;2=jiei*HVU-8$Pfv$eX37zH+Ct#mAO0@* zt#i-;&SEMde~OWilB*VE#H-K+$cUESS)mir#va#*ya?|I8RNM8oZ@u_g^N~GssY`` zVO@h2kn55JWFy)zlCFdpOLA>ca_dn9;lPc+x54-JePse+GINGnCD(*!5eG83`?mGo zy`6|-XcL*fuEa1T_HhH2IdwZ`w0W$|28*aL`DMs_iBIMe_kj|f4X;vqNFCk|WVkMbdKJ9Npj?ayM;G zheJn3FT1dC(I`X>&Lr@74PLcg?lzjPs@BO-9<3M!1=8>HT;-$*6IdmzbJvQ$rj3Z7 zd+F1U_;@u+y*ES~pKA#w+w6H#mU`*O>)bx0qD1}~-2;eOF*-s1k6{P{o@VTcTM^k0 zg-1GSR9fq6q3R1J3)=}Q1mYMZ@kjG&(7LZo6e^LoZv{^->G7w~INzYG{gHG|G|xhI zEN&SM$s9q$`gC|oC1#5@r^_Cp{V;wR8jD@BQ;`k}PK)A5rPCrjGDP2;dX!msWGR(U z(3$v`LhXIHjrD<7cSzS@2PjGq*>3->bYgsQnzF^9zxn)wp2#`%F<+U0pEqJ-aq(>p z6j)|;<)gh5Z`Jc4#A!dy+%T0)6mqR@gp7ys1Fc4*t09a;R_adefdywEQ^zsph7`;U zH#WXJbVX4jnIU=+F4GrhJD0G*Sykq(zi3yj=ZDu7^{2Pvkuy{tf&!$Wu(tE?9>OZo z7rV7w-=@bBPr}$BgO>6*pGp(xP2X~z->im5MGH=)=5^3#-oq@(Y%v7}nClMp){3XT z44ZZf^WKl|Dw(&)qxSET$R4S{ml%Fq(jxI$JE?u&<{`nu+k#ntZF-@hap2cOQkV6k z;@3VN)8~J+BN1L?<< zf^GW7!-_znx9g04=KxyM5-&O#3xYPqI~^!=L>7TwiNWf>#Sqp=EB4=Q07-O0vr8W| zV{xc@T!XEV3C6^UlPQ1c7#?ofb-?a>%ZdFkmrAN-P@evG6STkV8#U$6j30p-yDl-? zbJT-aOUqZS5X9Wp6KZJrfzn5*k4q%CtHbo2M{=XXUmC2AwL{q!A_!!qRzk>d)U!>^ z^_9If2ToqIgeCnE`QkthEC7u+e`Sw0D)&VPk3`-0Gmfc1eT$ylg}I5xGroI3J6`Oo zmGwNlwgbm{WO=qXif#Yko(gL5%d@?P;bbhT$lt8><}j1EO{)|!8o3ZAbB3K>T8VcT zO_J{HtpTXTq%qEknn!x~^f45RP6zM?zkGRGUNA?l> zms4%ydtcm$W*MdJ*p7OVZIR~Xsex{)UI|J$>BHzK{!Um$LAjnv0Z2OllGY4E+WEK{ zXLBjL1XzQ|u7qf!$fU95flAGdZ~P!~8GR+BoAjr(>a%eYIfq|D_LN@9I`krfp5}i@ z>u6<+*4q4?Hq!SYL2CbWC5HKW+4PFosnG~V;K+?$*9ULj^;ATPyy%UGzvBR9=&PDx z4j?oz?%067^cOjg@ooUhB_+LnMYRj)JiW*b-ea-xcvVOU@{ecj5z<$E$CjzV0jg4? zf=LUjZAJ*qKb+l)fH41!2mC|EF2BB`hvzS%Z-gMeBvGw=mw|t9JIVXV#MfUxL*s<= zKy7SItpxSrtOk!%4y_3+eS z(OZj$0-29_LsiZYs)&cx?weq{fZ|xS&!^C=nB|6ENj-1>f=g}N$zUQ@=?GR0f-EX7 z>HF}xjqd)SZ`)ha+U3c}kIqAK65JSX?g2ZF0F-VKRqk`to*h9p|3LjhJIB)@?_bz# zO=UgjFzKr3`qpq|o?@JqSV3 z70f^X)J9rEqxtjNL5%+|-d6w!t4=r=&Kl$kaky9DF&F(sq_W z3_G4ej{$v!>K!=orG7cWIz(b!LlLOnmL6l_06_>+z50b~lz~JSgeFDg-aWk3P5MfG z&P5Zqb|;t`AVA&>pi3(qdO%j}$A%UvS7i&p2I$a7O+O+1Zh1?;9W~C(*!SSTiygZa z2^-KJL!WC!_K4jW`^8qdp|x~$`0V`n z`enF>fl2m*2s;qEqAyrK2qk~aEvSNafX-ci>I+wb%`N(M9WzwXUw+BX?f-LxbLYkW zSJ;LB&z3<``-`GDF0G4$ysHDa*sH9E$-ya*+FS8~BzC+b9NRykiTC;We)@xsvRF1u zB3aO(`euiaN5hVwPDcP6{qz$)|2_z~z|a5l`463!+7Xbn^Qm^O)z4ex^B+35RcEm3 zjH{ow!>2!Vo>rX)tC%9NqcdIUfXvXD1pZ%?uSO}SNF*-;Rm>8uQmZF?yOuxHS{|Nv zvWj;oRE3dp8KfQqX{tt0LqYr&=b8dN zm=~25mb5w3T|ID0#oo#9-`op_0?+O?k=Xf4GVfNt3ifz?qbauiUF=?UmzSg3_Z1Wr zn82QNB;;)=qDAFQl0E5&x(gRW_t04(TAFlcrhXh${oA$fw7q!p3l2 zDcE}f)DnS{*n+w83m-s2qU>Ma4)EiHQFD3OpU$UckXI`{+GI4GLWN-eji>4_MHW+v zM3286a-TYX2*#~cm2*t4o0d}CLTwgmgO5&r^;=5u0b8oBExG6e@fLlGrjt|(pO+C? zW?-+6h0w<2=aJ#1(Eiw9Uy_(Wc?{wvdNxL*( z8o~jF>{|_}@K}2uJH8L9tOwU-4yWS&j9BTUMrX)Eqi02$4({ybc$o|{9 zjXFPwnky@zyWklkErO*p{%+XaZW0~ct5o7ADfG!re4^Fc7x|X*Y46l%7C~My;U=Hl|=;aF;pO@_Hn*Gk&; z)j~OK(uJhKkx^0p(I+n`(-Y16eYGXM=I1Y7oTBd%?3B@WEC}0u#w^jfkw4Zd<0@}L zbE7mJ*rx*avp)Vw6x)LkjJjqC#JkFl9}D(Ra43BF;6211wo+w9&H2Uod;4k%&t1d* zaj9;b!5=L%WEume6_+p6KOa=TKcwywi1$34-h-DD5ky__5~~|TN`(8h5HOYP@J8yO z7b$m};%5+aOwg|deSZjKvQA!x$%5N23u8>;^lO(-Zf~tGJLFQlMSq~@1Uzn|1Q$xs z@fpS2HJ3L9<&K`^eEN|B`ueY^p!jhrN`>>6Len~yt)7Bd-aHo zeDEpBMswzB)ajD_JKRwL#AX^fU*1Qx8)z6Ayp{g&P+PFFLzhva3voy3U?SAw!mYD{ z8eUsq2^3+?`HqKD*fs@q=lfaiv(qW3vvWN4Z<|G05TQ4O&!UB72txOq?8124pN!3& z^ioo%AOt_ens(XP1|N;BQ{H>`&}w=-xni$XR~y=j#Y?Yg$!z#NOXTViXXS!>!fzEv zSarj#Kn3*-gnqNh26nL(RZv?Rp$d zlMDA!L;sZr{xfB(^fJElGa}?X8C?GKWO#D=jtAp=WL6J?=WD;{}ppQgL>qTdKXMI<*9(`^)w4+e?e7o%9Pm?_rNC zd)uQ@y<~d9H&WN?6QP66w;oCj6x_eOZ^6H!G>x9OEohlIjIIy%k1m{DQ-RZS?w7t3 zf(U`P4ovEOn{qeSj}G5|FRyCv2kCk zgU=wCxX9(v69V4W6!yNotKiJ>LZlBAzw{lC?cnsjA#a$2NCwb=XZHmN)g-@4MtC3YzH8&L z`@ypxQIBt+SJTobH>raOEs%c}NUI|FM1(~lbl+)(+kJ1}%G}&s;k&1Ndx}ShEA-yG z+Aw*8v%#E#m5qo2C!f|X7>K>|N9_A%aQ!KvVC_9{2GX1>bG!u&tB5Nu(6Fk1oCFOk zw^xt8l}uP)t!*ZwIrAnnry$SQ)){I6LA8*us<`Ab@YbJ@urhh^!aMwLKK-$0jua-l zt$X5@^>wQ9Xne& zJv^;ln*DyK;us{@ z7Oq_lFBUpG53?69orUI5uo}nE*e(0aS0;rC4kI*1<8}qzV2I+D|KuB^g8<*-LIuw z4&_^L2*KbsbHLHkxkumH#{`YG9Ds9U8v7y@XVh}_$5>`JloqSLB?L8ShC?Iw@5sEcqSRVb-*dl!~ch(4>fHfRf;?*K|+XE_2 z){iJ=?psN739O{u8#d1h@osZtV#vB@avs*AD`YjpTxyFuxTgMiKJ#Q!HZ~}0AZcvO zde_FP=yE*{2jN(`^vAc?F8g-tR;jk(xh!SIDSB=zQX$?yf{7Nrct>hjo?bqEQOW2H z-pAkf+lmbS8Vx@ohCTdX^vRQfc#Bh5S{}J2{i;^p7EV-DFsZLR;Av^?=?#%*-%5H+ zEh@=A1$oLQC~^;k`pn3KtFQ}OA$_!khNc}{k2%uuolwi>99&4SM1+#CS;N9QNA zta>hS$Ui+4x~v1!t7FqFCg&k1WRgdj;VE0C-7>?W0er1-9BYq#jf%i zRI};Om`1vX6oIAgqT<`9a5M=qFxS{pe8-R5spq1IE+?mPT|LNy!yyj zg7#%r63_Q1%~0yedc8UkCs#VIN`WGyPs|ZLmNBD-EcDL~(kX+a{Qy(Dbvb&y<$|*z zYs0k^>zE308v5WI3a*9x;Lt|9NH6IBWl%AE@)_zQqo>gi$LnlS$d{y||L5cX44Qcd zk$3uzM;d)1ZxZc3dIk6FGiDI|m0}%NoJVe3hBn6VDU%TH&sXeU^$%6)a@6M#Rf57! zroMZAxt{896w})1R?UVAjM{tpj)xg~?0;kLJ)p8Wx2@rAJZelVv0y<2qap$-0Yy=1 z993fbc&@DP?Hb-18aqU~xEYB*jNW(dn9?J? zNfSa?q{!^;V_Xs~d<24q_{4&}#!d))nm_cN7(3;hV0c`lR%wPA)GL#b7HFCsd45G$ z*f1~X)E5Dd-zwUFAv>3d`UgkeSi6+&fuK|a zR`m2>3|6@Fozvi)^~VlTOoJeO>hzF=)_K3ZP4Nta&r@&0`BeuelITe2z%z zQIi7{^eV3zh4FjU0*83^gWASc%nEAEO`t&&NYfU7;pUiFGPRC$t{|y8_Vh}qE|P`q zg5r6fQ)NAhna-JNgto2@4JR z@D20BG*j%U6v*6>%=CKiJuu*`=rWL-W%dx~PX!>8@Un<>WRU_=iYUh5P@Lp zy@NwTFHi(P;_IZk1t~%q=uqB-fu#vir2d7lIvF}^@!O2K?Yqib-anmMSQxHw3{i&` zfeck8QiI*ul*p z1=!eWMCfS*aUtubNlmKOlSg0CzNrs_uu;EwtrEgv`{MPr1`I>@#p`Pc z5C`Im*H>=MS8mN0n}YdT0{m)MzY+nzI2T_X>sQD6Wfb<6wEC3_{57)r!dQMKR{=Ww z;`KGM`cH_g{v5n#o))L#W65=f$CvvXr%g9^&s6@AcZJHroE07-uJS)v)cWu)(!ODy zxMB5~t(tQdRi<8Nt?f2#`2|F;hRZy({;3mtXXPkdd`Q7gi@e%+&^mqh75 z@Gvbm_zTxcHM`V5t+J!zWYNmwI~@``-@NjY7z(4s3jSg>PLwn@PN?j6347u5yScSZ zV`fXyvm!xu#ZFWn62|C;FJ7UW{tmzS_nkcTEbh;*$;8z~%GUCb&mLcilVjr|`@84( za@)7H70Qe1vTr+~3)bUjzaZweI{S``WtO*(#L1I)B-U>~X4ZXa-w@ZpL}U5>zpUfX z@9m2MTKw5Z41N3cWPPzD|A{B-tK%R2n7>9SqgQ0X*O+hglKd}^qQCH`#Zzr68>57# z##`z4wh0#$_;2cye>k|#X2>s?JpZeih?!AlFD$vfyZ5P!ifxa&@ggiEKTp4Y9J_vncD(7k7aoPBjs-)Prtp_zC6=*z#YH+)9Pg7PO3LQ0 z>1^h*zMm-0ad+sZ{T}|0yyvgyVf5br`xq(nFBVcYzf($_o6pzuzJ;ZwcuARThxEbv z>u-u?41L%Y408YUvHRS@o12|p7?fxvSnHOQB<^}Xd<@c;{f|sp{(7rj*#qxmwVizX zTi4t-crw%YNZh)SsbWIsKPO2%nRaZz$=CUX`E7&j;hR5xW7c0?)X>lGssAOB9e(rH zuUqlco*DY?@3-PtuRLm<{*5l;>mD7o_RQBkI(kL^jTykd&5q`ob$q(bKetrnl+Niq z@V+8 z!o$N|wt24DvHKr!%l2AYrd5ZonGzqF_xO6-Cz0amEHRfy~c$;7ZXd0 zYR`@ldQq?@_CDY6Cu~RW@W&4`ktr#)|EYBFU2&qr{0~=aI(7SMi^qJNWbW+wKhoYB zuKm2AHyHo1ayItc_?VWu5%7f(qE3#H*AwALtNnIy=1m_)pgx8f{GWM$|2~xGYY0EG zJx0F%`(mlD#g>sZANl&fyx8*p^I>`-RTSGS7My8h+GkL^Y(9s(Lg3PU8zU_FJDel@ zeifF{y{R+Z>DdmQRnK-zQf%T7F0~C;Y;YX+>-mMh$}OH1vQ$Rln%}o_+Qmv8om=eR zrZzMky8duwb7y^ulyA*d^DNslZ7U$I~$UP3!3~yBWQ^Qzg3=>=bWFj{cClO}F6A9#Kb8CLaqj4Uz9Bh>;_g|PiZ*}q>18)wguHUEL3<_F$ z(DZ_%V?}S-pgr64{PWW@0V$tiz@d9sAT<{)#RYREtdEk~Lhx9x0To3>Mbc@biws$K!i%_#kn&`o6X(bJz$l* ztgNgpE#p}pd$B;PT>YnKCY#m;-%f>2*)AU=$Rv=o0Q9&vW|$Ax1wMYP>-9-bzFA-d z9kKPo&~}oWoVX5RP8HA=bRy*f=Px3+>GE zo4eUgk_?+0IJz|KdX}zA@DRG}SHx7gy@Do9k{x6LT-3CzcuAM4tUJr{CYqb++ytqG zYe9NTV9NT2d(i_u^`t51h?ecm&@7peD!5{WGgej%N+MUD-@c@ZMqqgo&MWu%_19nL zNZKA>wkL8ib^eRh-?>j+-8n&u{s&5;O>sv*k}6y@bUk#@3QnRyYF9eDYkXZ4{WtU0 z?)kWe!{4~dvlrdhU?6pYciZK~B`=|KbVbt68coRIO2QMSaLpmTI#L!aPrw_JNrNln z@--EbCg^H$aqu#chd@;Ydgh%V2(}rLFoGA0aD&k}FFKG6R7|w*KV?I*d1=r=Q}=Ki z{V<;}%NP-3s9DgKKV{=A+4F9Wl)Wvjwp_$sJeV0vwx1($j`9TDLQ2`-K&L=T)_7<| znjb2Siz#Wxl0bfsuL3$X;bsjRPL5B}b&ECgYywn@4e2#QM<2VwrEycsCK_V@grmHCTskci*tl; zrdo}DnCU!p&J;-^Ni+7CM=ToXJG%-VC3v`N6lz*(Hekb4hcy>R32t`b_IB3@ehAsa z<9#h@Gn!&>k2kTrqFE}tckeELe~*OX3c@t^R%(p?BUKZ|-ec0Q;18tCkQe{WB%4$k zV4ru$B|xPpW%8O5SCecAXK=?_Ht&>6P{z96z8M`IUD6(I*Jxx~8|b-Yc^QN|sN=ml zZ1(KgX*emDE{`TDo+#N5_c3=R)N6LfnCZvVJ8_CSW(p;W2d{;s+VsgVOqTt4OipC+ zg4gaUk4WR*7A=Vb5Cl=l+*Z*;uYNiM?KS^vqi^8PZL~_l*j8Jk?V(Dz`TqVFZrAQZ zcg_?F7JMbU6g981D+8$stPD)Hn@=aC;C4hyLX}`Q#CCGqzVJLYbc}RK2jNsbg!lH# zwW`jv%ngv`e|CKt6nvcB+}z9x*B@(`Gl9!QI=7A6Ed9rpsBpWxCGD^fX^=;8^Z7#D zvP73wAi}IVqYDPx$3%W`pnsQ}&65*jCKi&$g0Ef29yv3jQFaosF;8l=n0g-QRUB@v-BD3%W4d6FUQ1OI41R zl|puhY+y#~7lv{kE!Lv9&#zuG>GwN1Meg8$V}(IIa|ba6FV{~hIF9e~L)9``c_25W zczb7e<3>|c)91cYQu^Jak5>9*dYID?n{1D!?e6}z8B$S6?Ng=tehTak4ZYc00I9kI zfB0?4XkByQj|JE^e^|PH^fE=Bh2IX)#My#$>#A}e$MMSf%9~mQLxgrgBF4=` zBER#l$XbDrX`_FU?xhhbNgNOu*%o~p=Ys$4jhkK9*BCqFCT>4CQx|mLC$*5~z4~Tz zK7(6^K1O;I`|G3+`&}o+QTSbv7QtNxk5!a+vA?5NX*YWP7R2Ud9v#C}`HC79EY>i_ zny@<6ig&v?x#27acDXIhfXQH{wI{N)gTR zGC4KdbYBI-JIH?`?7OIulq;q?NYA`K783e zV(m-bk=qZTI49r@eC>>|HO8gl%Pm&nf6JLWj&;vZ9g4Q(s7_Dqy)4_lX2pdp`S#4C zbARxjv3x*c$0menX85+Qmto8{*T`Jxk!1WX&*^yoyz8Ex%B{?i&tGtoT^T$~Pm_3f z^@G9wH^qy@()s5#X&#O>CL0I!ETL*ElA{v|7gCR1Z(XJ~;5SkpNZ*nvcf_T_t>@|r z|AKYZEx!h-@x4^RAD;xfw8HZ4&iOkaR2TL~=Wi~@nv18sc*r+U^mxZquXz@AJqahC z_dqr+q9i(F*VZ1~!)7v4t~PHb*wqMcfD zUv}o;iQys#)A}-F`)q|!yT~Okhs1`ZMcgHhe;fHbROduAzhSDzE&655*4T=Qic|IN zb%6$wx|^&7LRQX*EHBMZ?Hw&(Bz=s0$%D)@KkoE+cFF0a*S~z*>v$W>q;{x4-3oMe zY;Az)Uv8niX?00#@$s^el^*$;zJk5U_PYFS(Fx`?Sr76Q6@4Iac015HB>jwy+Pvm@ zBX1q~TEyEjb278_m+_N17i-8DGIt>Uwpb%PC~G^NxTMD~O=)dj8->T;M7+bFeShEY zzrMmH$;jm0-Mgl);O@L>gLA$ufb^()l^48guu<*tFcl9ci~SpS)Q0^G3Frs}H%f)j zm79ZuwPTM~`mCb&3AcOV>_snycM5s>SRS@oQMB7&bzVU8$qyN4IwkUJ+C#6}#yiBc z#u>eAOHWn5e`ARqPk`F$xaMHVaP1dW_5GP1HJaCbwyZ9=Hd(Z`cR_q)xt)BwRlng9 z?Pl#T@#f+W;*k*rhk|6wCe(UNZng=@u2);xZ9QmKZB=-y<~OG(TwIUj*6>5e%XZnt z4D{H+gX#}7975>xQhO3pb>mkUd$;Yg$4J|EHxCXpQ*;nhZ`bqTJhpLQoU^1|PcoEx zGBgtrB2_?TS>9^C3dMvZi5yd3ndagGJtDq9(TfoNgSK^?S>fJCN9M!q${w8Dw{gRU z4Z%i5uZs0`y^9ofJvMp=2NJ`xD(m_hA$_YK_v$_E^{H#?$={w(ny6u28dV*mM{m58 zFf~_jUTLSYZ(;cU*7kwwjJB{H*^A~Gy*P92gPp6^=c}rQRkh|W+HLgzVpW+F$|09( zzhTm)kYetL)>2k>zr1cWv`bCmZ?0SUBc0|rOp(VvMb^IQ)DXWsnh9uM1i# zzsX|%KmX;kRnAZ`|a9=^VMVk*~w21snd3Nj4sylKMvd3bk8VM?-^dDVIIUsCoy% zp$wyuw~u`FJ4UhR10zVILGhP+VkZep2a2*j$NKu}!#8CwErAbM2N`cK@;S?IR@f9m zsK~u=u?(>Url6E59z8|0RB{k$*tEzO-nY~ z9w(cRE6d{`q_yRvB_XuEbj6CD)}@GCQ|1}RA@+q>_}>L?y9i)$O&mXI&JPIwR#^4} z;cX(ZXza6fyeVQC^{03#4s zoj->Ytyv7I|ZQI$x=I)b-&*o!b_+Jp1qe(`V;>^l$w? z{7+Mvt%0A+pE^W~kJc9~FZUrh1X)gEK(#Hz7+Ec1%XmM(1+#>-xS)BxQ$0&Lx-~

    XEV|Rb%T;|&grUD+C zBYxZ@sh#jAipT=0d%G%KbfMiU8uEqEa)KTO!|hJSwu{PpPEM7aBd8X(T`r6;q(v^` zZYJ3XvoE;`=94n}{B=i{TG|1UAUU~!>UqLCG7Tj$_eMVJ(5w*g$UKs`Rx%4z2cSm6V7M`EiFLD^4;E_`+>EE?r0r*N*+IGD^04T<( zmF(1a-+ecIpu0QULCc>@y9end2T2#R_h$-mvn`0#qp~GGq%zF$3$ijBl+6ieT#?w< z`YOiUAjU==zKo;`L%qK^ihXRtvj%^Sl9VKgjb|pEw=A`~t=>`wh5YEAy3iR-wJ@uv zPew=XK%`qalsE%dA`!NfrZ=&nTn+$Gg!R&03D`-pOYBM!x(R(v73b0UrSt&7XBP#R zExU-cczwAKA)q9Me|ol7kWxcSWm*~}`Zzaj+BC8AjYXkm4&>elcOaOkl9HC_8j?OF zxB-Bu;CxHMuOK;@yh~of!=&u@2|Qc`8U^GDHndB9u$T{+f<#ST4Gma2;elX&a|X%*198 zv@D~COrs#^r@ok_aY}gJnj>5;;;mZ|aXnIx8olPOiFNvbOuKRi`?3dI;YM&`m zfwgfYpa!CO)0_4}|53vtU&Zw0jx{OR>0G35*MLK(sH8NVoDK<{lS1ux6VBKjB&lME z94@KkA2|jiUtJ9-8!%dRxOml)onCWrX|~I>6XKHk3629ysguuH#z$M=m+6|E;n4z8k6@w2k=Y0%kPb*4ms<&O z;|rwno!1`UQ7kw%ADN86-plLKS(*(HwBqvKBj=Hh7K*7N$bc1^ zW?Jo`w&X@+Icb3_TEbiEx8bBtRr+r^P?-)$ z9=(MYB<4RuI_e6E%8fcL6)7Xob(GGBx_p9~=3@kjZ0-BdL%I0<<3L<=kovQx0(F5< zE`QU?R8t6iCu2YcA%UM!<+Bz5tC$p?4$oZNaWeMwUz7Uvc;D zIWMk>UO+Ur1#*`Zt~dcAl7I}QPi^GNBQ#G0d+j(t(aQeLG8gExr{X~AU?)$TmV*yG z04quZ6s$X&=JR#d`_>?06D{f9Bkw%fBbsl0UPLk{P)N==|ae>M*i{ftE=jN z#!uVnv@p`Eivaat*u+B8pQeHm6&~QTFp|_8Y1{>%xtnk@m(Ua%oTaRzF$|OU2Wmpo zQ0%xsUK5!nkc*|)Rv+fX8=NINUM-rkw#xMY9?q@IkJ{xY?vytmI(1#fuAY?QgpQTWuB^A!o zE~MV?bu?Gj6q3@LQ2=_W0I1`c63o>1vzDE!eR+GZEt~XJVP*q62y+ky)c9fcM8@2TC5p%YFz^HJNP`5I!a z>5DEYV@;l+C>9ysXYz)uI8K&_^&Ot*vCLL)%wtGHnlppk`W>zK$0c^8&Q9XqVHl;6 zce$8fQuDJyD8XjVp z5Rx8tZgv>#m34yH?VGL~7yFry-W=PT7pf){95IFoO4xfOh8H7p6dTiee_79*sCtc5 zj-B8{V#ZX}5|HS?ObctO(BuH4nyE5@k>1fIK80EEXsr#J_yV?3*oMa~NNWuDK*GHg zM%>CA!z@?`#PJfhIUIgVruKlynJM$tt9gpYG7Bt}a(c%w3U(J}3px8cuM=oW92_vT zbM649vjvZqX4CKoO<_)4YyIr5XIGlylrFr9s1!Ih2e}xMJ10SM8Xx5kRr(x+ny(V< zY-pqt6;5e54XpUhl9L5cwN{3$l%yxJN(#mygstgO%@U#P9L!%hhZ{`wv1yu1$RLeG z!EqpSCwwqBK!j%y)w4}F3YC~lv(3Dk%!KP3I!k=Mwq~}-Lqr|#W5xw(amS%tWcp;x zgvG(@=xu&Wyy9V!-JT(_Vip)jp)#6T=f0u204 z{yilu-3|D7Q}W}yo*GKk-0$D{yEs!JQI^kpCh5CJ*<-j=GHS^F2nu9FW?|<90g_=4 zH*SuSQ7(umo1*x$1#7TyVa1KJ+^u6R8HWETJbM}r*<8 zorY6Ul`ajjoOa!W3W}`Ol$dO!I}b#-reGoIg0$vvRw;I8u&M$ql3VRUJa@zUh6fu6#|R76K= z{#`J|&#xON%*6*eB9vV9-S^*TzUhSbOu_6Bl{z-PA@TE{Ps4Z_!^P)da0(7WuUgoe zgSm?l5htc>m@r@Y=Zn>uo@q!`r=c`aq!u=N=FDXJTpG!sWHg@fv$o})z_`9!`r&f=EPX%RU&2x35@`q`4Q&~|g*Sl|v^?b*xc58?k$It* zB*Tc(q=q!yfam+Ojj&pGft20Pdmno4Tx`?hP^G8g3l{?H|A8ux+cbp`&#rHZ_D>c4 zhH>TkY(;Bzkj636)7?cG!G#qlbIO{-?g!*Tm|N>@`2;-h+1)JzXA(!x3a_DJ{a~7 z0Ah8>ZV*XEN?Du1V;&g7*teue7c|4Xx0u;3Cc3qlEVrP7_$75AEAs{Ndqw3hu zOC*ilho3m>0T&~Wn4A%(wR`uKw{rMXQE(fvd7>wAf*B^=k`(cSjo^zfhGV`}zGES( zyRVSn%4zaIhdm0e1+S~sQXw2Wf;JWffUhuXAY=X$ z8Ef~>y<4c9g1>vBcxSU`Ke;naBUtJ20`$2j8twtS#So`8a+*@*r)CgkB;Rol3ZCph zH)JVY8SMlNU`W+85Kh8Xu^({JsxkKVf3Ua6I(g~K$o(Gapt#*qaX}~*CRhjNxVi~J z!jTVn60EU9fxI%v-&syKP)26^Rgu8w^(3B-;SG%8qIm7bNHTGS89gtKGEDWe_Km1A z1Z-K|?m-3|pL6-Ked{y6!lg8{N5ha&l}X0*1a72VVTy-vZ;P*61IKa`^v#;Oj=Yk4 z3Yl87BpTg?+;gyRC5!}tw@GR4?bjDY{!a80Op`l@6;tUErc;Gk6~sZQT$9+ngT`Z- z!g8ji%+~fwlIzZGts=!lw^t>MV&}zh{=~hL>aDjSmZ6~8T`}{s_M!y1* z?A9&DQBZ9NBXdtb(efd77=VKWuisW-z;zo#by zV@<-J*JB(5ZAl+)Y2eHxQ4ku_EI&T4O-qh!;U%5RW; zXs?6ZeOd(Ol!2Gfqn7Z1f-EH+gI$7s#C<{5LBy8Y2qZR~KtfHt90s~&UA&v$X8ZD} zY1>)EwHNFSEI9JG_Et2K&6NrqOrDl?;9{9*g`x!q~D$5Eh|jiO_uS8Oe~ zQGKYcd!d`nf$nXT2NdwAhfU^&w0d-1NT{5q0}Q<`Ac|U{Z<(#%+38mY-V7JVG`k86 zq3F+u0v?bCB9FhHpWo()PM}`9qfN@y!VowumxKY~1mVU|C|ID)^E^u5qU8qyaAQ|DflJ^>EM z1>>1gF+xZOyM@hkfn(A^3GC29{>_dH9INt;bmOhg(6#Z}hW!|`xx9_Z!t;8|OktFj z1g0)GKp3fRPa{SV-h8j@nPV6c zA>ZtwgRC5|^A{esUn-By8Vjhb2=c0@XanM&*AbdLy z^d&NB4(^*GAY#ghWYlPLp`OnY&F};!C4R$X1ayYJ!7QBlFd{43+EKsSO@Y-|+*Hxh zH)k(nGFQ3#Tg0#{4fksA(a_)m&J-B-2wBo*Q$a@%60QX#PhipmiVG-^wdl4TwumC+ zP)nWgO4Gx7EM8%_ddNe=lq8k1x z@-I^gx30?bh3AKBOW{1s1_wzhyZB*0<*=aXMB)8nETNH;T@_TmkFeKk_yhk}*& z0qtq=Ozh)L=x;RMf(cs{QiE+$y4demSA5U3o+*`Y_A~@Dwk{%`17%1?g@4=gJ+5PzFL9lmtUsih@{DRDc%DgK7F>G&k)MEz!?PLTi zyP8-+6Sf_t3M!6+;p3-rb0G%^?WnwTmu7q>4)!LBI=6u*vK+f!+x;=BXETW*=_1*) zbpP`!)3C3WMi~&c#WUE#Bm8sHDVD89nWt0z7-qnwv^n&Hy=Zg4fEmT1JcC_nB88O1 z5#5rCQv;?4Vz#(&w;+f&cXT z>&*pd@A?%SC=7cel^7(}aPMgarjCSrj6FkRtXOcko-%)=xgi@ zJ4#Qq9QxY{=~1ft=7g<(XDC;^b{)-jsUGNv<>`O7rNz71makfrk(M+&&>fmGv)`aF=0Xf6pw}!v0Aq3JazE;vk=_^7h1%GiOe8FQ#9Eod5HS5IW%hmRn zOS%PK(#TzqhhyeU8JrBt>ssIglCc+c0eEQ_wGH$(r->G7%z#07q2R$N^uFs4+0yK3 zOL3zH?pHrd6T70tcd*;{VA!{e8LX$rrMqN3BTpwpnkf~j6@+UimK=Ykpyfr?d|pXL z!RB4RK$?LgBYn84Z(qCK>+TH(&9Ihr@9V=6rWulNrSWQOTgGjcNd^&^j$d^cSiKSn z?RSlNc?$uX&xXr2j;Tis6!QL2x?%*4yVbJqt|mMvIErdvT9`gKNqHO$dTDsi2m5C$ zF=b`Wx}yggO5fagqK787+|h^;A^sup1pDB~oK|Lu`Ku22sXv~& zoKFP#A{|DjR-`qvdNS%{RkVu$-iw5TbQ~pTnn6r2k#MRL^0$mR(Ex|Y?aTiS6Xn!) zGT(Fg@Ocl(r02Hv67mj}uGhH~Jy6Q0)Cv$)=ty+=nNv1|jf*5|_!&--`0z(Gu=-35 z8UvfFF`03l{J}ozCD>|Qo*HJdR}&9+gR6Q0oT;TKo`@*SWvZr}y&NjEmjXGxv3C0H z2p|r@abNYdz}-h=w0r0S_R`lPnzT~ISf&V;*pALr`bAsuA=j!fgW`YbYlwDvX(VTp zH6|cc;2g^vW+70Mp|n7A5XYwy&B*5T=BsJ$dLXN ze|1d1_NW)kgg;SyYk@rcMDdc@6f_H5HmaE*Si$BOyM{0L%1=L*_YjdAJ9nF57NUq$ zLYYiZ;8$FWDD*to54ZDJUK|x$$l2?;m4+X)f(fg#o#hFn&?6gTHFQunc`1@lx|BJ)JW9?C3=pU zVIS;~)d*=1=QoQlu~FN2AV@ot+L61;z`-iH#lm4npcuQjWF^Qf?p0Z9sN~dUji5f4 z7DpC~^E2{Bnv{yj9G!bc)j zj;=*zMLT;fJR{d~edt;cQ^-ZzW3bV!RStmgW`ucK9=H;+2bsWCf>OLG3EiICtDgvt zGMUH>@#Hs%+6d<**m-8(<`x8X|@e*E@X3nraMJ+DV4S{{gj^pe{C$k)K? zrsFITob9e&5Aq{H%!sKxf61V{t&j~nxMT9vjv-J0!$YI42T^*6y6R~cA=Du;=I!xj zfRMS%J?ZRZcxDb8BPBNB+% zeZA|CEF>@pjv_+unVXUkP5fpDU0Kw*vsbgklf4?rm;yDLbsZQ+KEJrX7E~4;%*q1F z(Of2l_78=-SAY<>8PTE9&&(fAQi7@a%>?ZJM3+p^3kr6uP*djN=*+eUTCD86k4J@9o?VEqR^IOAA>!?6|- zxOXmRjNj^v;Y(Z5;X#P}Rp0wgb64KVh**8-)enRHWrLb^MBS-CHhNcS8TMgj3T>iX zvzFn^dmT&sk(42n(jDtgPJR1G=XuCN4UHS)58ta1e2{G>+zMGNn1Hh7`4TZZ)e$_2^}X(C@T3Z+!fm3?M` z+Mo8pJ5oQyKE zqVuzstIHs=*M-H50PNg_Qh^Rk3Z2bUiQdt0$HE9(i1qx~XFTJ|OV1hpqB<=8ebV>j zUluPf1k_pc!xX0K7mf7~yo|VU!Ddr`R;wav5%ngO`yj)~nOZW2k^L>yP9G<&(qW*+ zp#r|k1r!^5{lh@!T7Va);(_0U%?#x3#fD5>{-}gS7q3QaapL6Iacxzwk_5vPEl{&b z{MHxyqAyfq{jj4Vf+!>Hh#-AMxiu_uuF{rGR;>rW{pUhF=+O6Ta(P=RSK$T`Ppl<* z#--gHjKbb`{lLn!!&tAl(kv!iye`PUf^aaJj<__c168I?F5D6opq-ybbi5h$1sznc zgblL1Xyk10jXm0&U{HF22%-`;j#)6T$_y6Z$)Kq{I?nv8hmJqi6{PQ@{b!2dm6w2l zx^sFc8ltGY(Si!oy2BIPfJgen@21hrx{^|p`Jc54>+oWgR(SQF%okbH|lcH z4Uw5T;qe_Iig&P4QfL-8&Uu+GYRjl@OZ&V;MqDLH(f8Ifw(kVtXtSb`$K1p|$hWqo zNB8jckqgurRhMU4f`cF5dIQ(~q7Kr*mqHs#8;K)w?i^}VDab5m z5G#TPO}g=Js{m+o0hEGQn5?}D1o&%fjJc`&2ciheIJoz8N^gxXibVUn<^r7fXS1gr z4e8zP$uL!OB(#sc93;<~@WF%ak3#PO!SFvJp_&aIby!{{@tYEng!>~>63v*3(AO-c zvki5%Cxg$T66C|Fig$ta;W`}i|J+gdK4650tmhA-PW?`m*a9z(@-x!QZ?g|5<`|%- z6VKCcCq!bKQi_y{NUD-HI@-r12-ib>VGqW;i*kw@&a`QYF`!b6GmT87suyq|(X8(o z*X6Cj^mztZQvt#&yNp}}Cs|mQQ3_xEQVHH<#b2IBgW!70Ca$vH(#{H-vR^QTJp>zR6oMCVs=l^oIt=&xiG_k8GZ;*QrTy+pzHITygz3xz2RZ<) zC)-W}+s-F~6Wr#ev;V~Ad8u?(NSI-i!Tm)S@xl-P@gLk zuwgW>7JJU;U)`+*d$*i4v2$V}5Fqc|J~qlNMP%&$7Lf@bqQDa+H7v;H7mlExhknpE zWQFq4qscG_6>>c1{0);f&2tWrxzFug4+7*qG9u|yht7ldP+(0BuqYjR#1ggymce$} zCRkd&hlh%khEhrxh;6b*0FnmKFArPK|LW7INekU~tIK@=-hkSR$--_X{cu6@UE`2t zsr)ytXvx^|leR_=AeahV+zc@4l8GH)K>lYq(Akuy#Jsqnk7~>YZKNDh^n>oFtXT|U zIXSs{Xgt7jzPQE#-o{qLEGnQ7E5xku`|rLJxbr=8BIIB0{?wtoAEqf4)7L#rvf(Y= zuC)RA1s@n4uQG@uFI|J6CG93jlfOTDhOAdw)swm=;Wv|hZ~-r((6@xQ5Ui>S%z&1S zHUNw$`)nKnS@s#f9sP{USx-|v^o);E0ECz)L$fSWe_IL4VY1KK$qFMSRBf_`@2f<7 zvsDgUGr50tZvxruy#Xiq?~y%mA|?OTnQ0xm%X#q5)OT9$Lj@uNHt%*B$G59$*tO?R z>^zEOB1vc$EZJK6tRth1)xeOkG8_7=|4TY1O3HQYp2!}(Cw^!5L)*;|;(GBE zTwf8^717)*BO^Y{Jqkz`0I7oTL}WJtQHk)s)R8R%Sce#|5%_L^`9PdVU!a0S zwZU5mBLjLjAs>GC;wO(gWSpx*sriIiP)5}^v^sJFT{e`f@CD&O7%0X&cqcgkYzkKF zIqayuQLWsm3y*n$f7dNBx_JLj9$AX@$V-P1iE)%A3dMg=694eW#vDYQ0m+AOc|_Zw zWc&c~5+y_q3g-XnfFERCz^u6gwIX1%kO9LMytRtTnv|)UB8N>aqfTt81VmdbU=I2f=jqd@`GbC7 z{Ep?SsXF0N1ikoR&)WPTJN7DDuaeVYU#{ba;_#221V3F+$Qr!icZ__IR%iL8BmeyP zwThaAzIbgF{Q6T`l&|YBv^Bo2!`F4d3M@!Nk!GW!f`USmFH(d3k7b6I1wQuTN1HVA zBYX|;`1(^?j{mE5P>E;RxTuhoV+>R7|L4d>up) zs4@x}kL{k5s(u4ju^WA3#Ic3^$sDw=pbC0S29VHTw{ae@rD;?nMHI6CG0PXDzozt@ zBHN)?P$01$sf!jVNQ&25^9rK+gK{b-@eq9}IgkUONT`xXobzWVuW3c7%Jh~P*a&3B_z z^}r5n0W&%kRowZ++9uHy;^iQyh(KzJ4np=LfCjRRIxxcs_G7uEpcIH9x_c?<-he(8 z7hEYz5=KW3adfGsqJv*Uhbk8wEroHUMMSC|IY=4BnxZSgad$pPE!$BAbwpGq#D9sr z4;S3EmTg!0*$6Nkh|fz&h|kTRf#+{h6;kz@d%?4t{uP|+6(BINoNV7W!Nq|Wn4Y|bAP*;)!c80gt- zCoLUFHmEd;(a!s2?1WU(NCJv_gp?G{s|@%8F-@i00Q=-T3f-cB@l>kkP=_%|B88!J zAy(zO(M`K*&={$lFvu7pg`g+R#2;F2ewf0x&Nr59Uly1>Dj8PE)3ziN(n{i0#S`2% z$!td%l$aRQm+HY?h&v&r77i@9F7e0%3wVa)Jq~ zOepDW?$cdb0Bmd?%cBNBmjhHPAZ0{1U_n1+Rzt57>N+SMuw;3Vu*x=}Q%}x3>hSB; zR4NTfEdfMWVpZcUjK*4`Gb-s2Z9FWlHG`_#y4ez)+vT2TShu9irp7y$M`iZwYa=?r zeW7x_hUFWJ2w<2?mN!ugAvFTTob(T&URLL_{w7N>*jxfq`jk{A?8BNb$7Xe&gFZ;7 z2}0f`NfTS1jGZcjfZuTD?MNbZ5ZD+h-c?t|F>6+;kVAJS`xKEQ49595kJ(KPCgo4h zreUG>`ips(;*x07UH#O96|mFW@zKZ4boC+l2GqyaSK}~XkZ~4dC8x6!`eSEn9skj~ zqhT(!3A(NB6R6;^7=WhHMOX|exh-#T2N zOcF{myDsa(y0ujD42rFY{_|H5_vQBZv{G-4Gm55H));FkRfeGekR{&soVdMioSoX# zA8A^o@5$Sr087dbe*3B2U`s1iUe<@8mT6N=%`J;RfsqZpqCUU)l!wM;+;s44QPR4) zA}|BEs4gHJ>Xdy@B?!KtZ(kK(%O&5w#k?(qp{LPShwa@V+ow#8T)Not#0`B=1y7;j z1zQn;lKF$G3+JvtP=bi%E#TJPUOYgZkw~|^CbiV^UHQ z37Xx(Q^O+6=CS*rNY9fPzybN);rp|F<{NlhsG^P>sML$@6mHO~bg1`*xX9$K*^bW@ znw+Er26A2+8f^tHwx!N9W~KWm3C;`kEM8=Yn`is&h_Kw`1N$vT*FhazRD)l*kO+~4 zl6Y#{$0&6=;GRsuHcbUN$4$k$4;{r}xn;rRj{vjGMrGZF?w-^EPzpUqT}*WMO>NYO z+1HgOk)x%W3@x%%Qa;EkpQ9bepzjJNUA9556-g0KU!g zo<{@+iZggWw#B;cp^SX!C4IRV>T@O7+Y>vf%ra0ZHL!>*KV-tTM~5SlnKsQd{l~M( z=os$Gj-ri(qpyQ@abX~`0eM8@hcsOscmOCViW7;F{B=@1Rb!uB;=_?GnT9=c>~!~3 z1gB!Ci-jm#?!o4a=_>6^Pz#PH6-F?MR;-Bskoyj$%Ruh?c(}*0W)&a@JCd&4v8Iwe zCk+RG#?4dru?$4dPTTaN$xf;#$EAW;N7y}{vCUKk0{3h1Jb6@--}vb*4twQUslK02 zJ2)5_!ilxOiF-aSh4o-txnLWACQno?X^)Ub*s8S(5`y+Wzz^-XVuYI{S|ByAYg9f~ z3)7g3UhQBLzuizLVS9#7^7Jj3BKGGAv(CoS`1OQ{bP+9p0;}M5s>FhH=o2~H!Xhqdcy@g}+w{V#zONBRsgi5p|hn|A6Lz-{t zzb&c^2@P`uM}5GikD`vQw`&rwn*6XU+ebnd)mn0_0%?P}ILH;~F?2@Zm#;((J*V)5 zHYF&fi)K6;R#dCBOzhR9wymK$GY+T9+AOqt&!VQLBnfDgxKM+#@KC46&OluUm_I^# zQb7n2DCq@4NGukC`MqV6hme?Ewf)46A~@1CbNQpiT;Fy!BPijZ0{=y>wIbyS6vm)v zE;#>@c#W6&P6{x}eZbx!ypjXmKt}^BFT1-ZV+m$caf_wj8{so$#Y~J4uy}A6cLX{P z_-I1ru?5r;wc4ny6wC^Pkw(OMUDVvp1MFME3!HiAb(I9chF^NYe#?BRBQ5~*05CP`SYm5V29jAm{TG0DZ3MFtzvA5^q7XS zsrub6lo_4TGW8=Mw#z2kxF8Z_$U%kEu$n)UQVcX6IN?#GVaeYJNF0OZC0g+*8_*Qk zw}OsjnEwcTO|lbxaPK(0;JgY$0*yosqFR;p8C4?BVgI`z?oL#XS-6lWese?(m6Ifh z+<-NERJ@jWi^LL{(NshU1jznQ)NP~AhS)IJ$54@s%5@2@WA4CsrQsm&X|m}@WOI=! zDkxPd*IrRkeBLhiQTm!#@u+`+V+Y=I9gQb| zN~nkps*s}f=IM2#=yQs~UR)0I6Gi zH^DwXco!e-Y{RLEJtC>ZtWgy^WJoR5xJ>2s@+x)Rkh@g&C(%>tV1Y4MQquN(Wp>`u zEt|sSiRFUMsIU%dvqCOGN{h2_R`Z<|e{mfdPdV#F;ktWN_lW8;SIe_vPolunBGz5wzZ%Pxfq4T+)6dgxQ{fW zC{$mOm+C)CLO|s{q(#vIF&u%koq+_fPAu$1Qu^Hl8`!imH+Yi~2&FE^4j?>FgWnA0umR&>VqtH4{cMV{qI5i~XD4=TBK6#ZEd%*j zRB0FYz+NwbjCgJDlSY=ZR!C}SEF`! zrl*h#wiIuPIn1l_I<3cXpx#WJFtrh~K&H=lJp&eZl7%@{Pj^I;Cbd?myv_5}vObtw z$xWIvg;Ge~1=9L($zJcdxsWW9nuY4xBb(?5lC8IK2KqNwWt?P4Cq1V1@>^h^e#q5hI9*% z9BbDeuN493@>D%FcTl3w8-vWPgb3F$_25%L=H>XJS}VGL_eqvuG@hIuCIl^w3}HTb zA81Z-oQzvYx)gH_yi7OeRyKh?Jh2b}LTNzF$XYLW^qG?3o;rXIOD!84AC);%!?#nu zdpPR-TB!b+6=Gw5pm51*KA)Eft{-MQ?5LOHcv(BrR|e!Gm$uC_aiBeXVxdrML!^84 z5rmtRVMBniF4UPy$+g0Z7Iu_544$e-A{1u!TxR1d5{@Gbz(ZG6RY0qE>=dyD<-OJr zsAxgem{`gq%Sx(M!>@EDusya#+k?}y2|Aj(h{O+u1RVm&(0)ip_UV-M%a&VExw0e* zn9brlE!r;-C71W03s~7BGFJzNgjyoas9nU>#7|v>3k2|qg?L790&2t|Kb5C2BAGp^5>}A92Re3mCBXX} zdi^C!3MBa)R#H!b#6&5wrjq4x^r2B~ymgnwpaE#f6ANwH>pU*6qka^#LSQvi_zC_g z6;Uu{8^hvQ5LKvi_f(=gPim(Sn(LkWB-FxWqX&?h@JbjU6}rUTwhi=bLCeEP22~tI z!P=urswK&FhA72_6vN#qiR`PNSJt&TiKS{2k?|cYga&{KYD9U3^OjpbayNd*LOiJs zH}pCY8D(51nb6{7oQzCcyS$WAVw7k7!D}_mQ{2KT#ZccJ>32ia<4W#2I$EcwyU`f{ z6GS!7oulQ_7=)PiO*nJtsG)vVZjH_-CCRNJ>i`d4Hj$XWF_v2Sk$f$)6w5=KB?&;o zdb_!WgK5RzANvM@2?@GhHA+cIQE`mIlR8yTR7#Hul{Av@36E=)Q_`J+4QbupGgut^ za*km7{g$U|7LEOO%F~4l&;4`EhM;rbE6h}wSt4+N`iA^&LA?l$-gZJ7u*u743vmMZERP`bL zaSy_b`=!r1G$Y47sLMo^@LEe8;wY=K8+ac@)yL5$I#_Ro7m=}u>3pdZ`cB@L*5*H( zSr>v={vFgE^~L2|%<~ZqdQG^8<=1Jgdd3PlK#bi<@;)3Taqg&44Z{PeGY~+|kvtOh ze$W*H5%y{t5#W-3s;p4<(agGiOf!4%hlMa)6KO1KXJ zex-o6yc(`7Psmen_wdkL9$tj@*4an{pCLoq>C)v0x+uR53_qQGvPzF=_Sjy(5o?bNZ*D zWC#AFElJ801O2@cL33L3aW)BvED3o*1=6$rWSd^-> zB&4RSs`_>x*cd~v&oAuNRMv) z7oab(FTa6L(=wje(*y!)exhS3jzdqf|J8>&;LN6Bqc~yFwt#egS27liDX+#c6K+EE z7Y}aPvgf^1_)8DKB@@m@Rw9k47HA63C~AOEZZhC(E^^kk&=pvFePjno_kS8zexiRn zX@&#UA|3AC=#!>=k#e*HrE%So*;@Dsb^Fr><8-)%V4=6~$VO9awf*9Q0Oxa;DUs34 zJnkbf=K(;@i#0_Z;tKk`>mxz3!ZQ-#%m>=f=29qyAOyNdPX|KlLL_u!=hKg9mar(! zhO$k=Yr`0Znc98yXH1?ZwRVE(A#C5*wNyyRg$SS2)kzdE%oNa@JiVW~GA>YtjWqQp z!p%L+2HzXs-E)5l+C;VLCF(XlY|d5iUtg4@Ihpr_$KmX;DjUCb51h2mE)a1MspI3? zE}R?ev&CMjsUIY8vAViC$_FW6b1DQfrW0a~`psMG!y^|t{j)V3D?{1e`#*oD6MpX8 zt5{28YHWcS;-FuQ>wbHklwUYskgOfc&qc1Q7mtg6eX)kx#B#1r{vC<=)t0y$XQ zRr7iR(wdF)R4J4v#D$txTd4OK>#hT+GzXg*e~_jSV9SUbe>8dt-+qLpo@}6YcS7Zl zlP&_{7*pf}2K~>`nC#ZR6MWj0KH+;Gh20V?h=o7Z7uSYd5G1M0a zTO#cY1@=B~K+^NVCR%W>UMTjrm0jYG)LWCZ49i ztW;rlw;9RKAe!-QLDFG9{qV@bNQZr9-8S4otU}Ej266XcJ}%Z^InN_T15*l5CKYbE z{(-ig7~mTbAF!D#6;)I`4qU&s9&Tl&4?^jeRyH_0Z>YcDAkG{GiK@o_{@>BVui97y zP<1Zqjn7}d#t;sj()~5BDP6hPa*wbPgn2WLjD+vfVW01&BS72dW~26U&4M}uhW>C= zaj>K&dVi@o#4wxO0I=V!(TXqhzQ4KySu&_+t*sr1o+<#Cgm-{r^mgu8g$XCY*IZfI zO{`5%%g~SzoeXz=tE=AsM5&iD?&7wLjEsv?HX~1HmBy!;%_gqv(VMo?2Q1q4^%s$n ziGTN9$Y9M@LN(CXbGINi|18j+{Z1S^yu^gDmn{kI#0N*!P*Q_bQ&Pxy5b7~6j@zTR zWebpmw$_dTlC7JYtvy^V%PO-&$%5Jo*7{yoT4BFs#320f{x3^{D%sp@APx7k-eE^j z`_0_4u3C8q_4+q;nE-@6Tl%o?Tu31iS7MIJszKk%bLZxu0+F?78_HGMIta(-HWM4{ zwsDgS21tMbeQHLCL>aNQ?#cRwUVfI5|{j_$WhPl5jW@QaiCQ?MYGz8sdU(h7&# zXq5qQuesTz96)?K^~(6Vm*mrMO0tjFkiH+u0OxELHHTP{o;b-S6L!7U#+XVBFctgb zUSgBo`8n)l3c%M(Eq%z>Nj2haLp(w|(hPOIZ`FR|=1tOY(`xJ{9UpWGY>ftca_AMa z@YCs<{0G4kASG@tE$arsx01z$;*j|f{?7S=TE|GDAm2w#MI{CQkvkk>V|P36gkpOx zgga2F&lWvWB1ZVgMN7GD+jQ}ys6hTW%+s8}3i6AU-UvG0HW<_cYtG_4gdR?fRrnpg z=Eml*n77x4P9ohN3PD>`RPrSg+cBSHCH0fr`Rwv0g~d{UNO3O)Gms|DAo}U2BRnlObJ5qAJQ=&cZIEq$?tQOW7yL)=3aj0ob1K}{ZBMa51y`jf*0Neb4 z*gm8q?#`WMFyL`)g%o=?YR@B8Y74V0G2KE12;fLkc?VO$<)hZsjN=9fH_0;|M@C)u zVY3Cc$qec>Lzz6fz&9QRR1qIFWo0&6L2`Jv-WoQ5g9M?(p>Z>}xgCuTbnEL9&Hebn z|Ag}wt@I{niEvsw>)Q_=tOCE614v!*?X_3q`dTw{fS^Kv>?jm-rG$*B-eLd#B#&O) zN}x$`aU)Y6q(6(oq{luqDNmx`p<0qX6qIU~J$Qhap9;MQ3-~JIQHgn|YVHDucpN&` zBBe#}^Xw2ABvy^PPSrXH!f3`f7Rh7iZ{>h#3skYw#gxWu8QV)f=q#y}1a{=6esDfW^6A9$S znldiZfulGr&IXOfsibZdUr&1j1|r?M8PLo*;!s0|Ru?%Eb&9FS?^0BpMnyB~9;d~m zhCq#DMf%jM6!d@EJNuui%P@?y`BOh=Q*nd_Y-Z|wD+RRU8|Xx`2y4C|s3AfSwK~(2 zs3qdES@I&I1rs!Nkkc7?f@8Ym?FW@|pw;m$6^{lAzJvsEL_L$!=la$W+xiFEzWj7V zVCUT5=Xvh?zOL)Op7Qc?x6At)n%`y?zRll1rFY+AIhI_0ziRx^jP{{&GJ*IzSZ`%Y zc&0A7xz8CHB*BVN=mO$qUh*IU%_beERy(K&K)#2ql=BAMK)H9vduU&@NqL*mF}r8Z zxHCX?IC9YQsqLF*qepSfZ5}>6m}AM9phP4cUa~YkK2Q^XGRKg`qSv+&eD{3DxrBI% z5#7r33xD7lWUo34<(ow@YwyhM>gqDstM_n%c2s!E@2u zauXljj+4_mZOf61VnE{7@cud8UG_fKs0s*Ro@b>8XCtyZ+%jQw#r>B6qao$?B`_k zRW}ag%N(wyMk8Y4zIoV&H!ZK5fsXR1tXWxF_4PDyf)V7y5jU*h4)%i*dUWFU7ZH-! z=3T&p=9tQ(uR)s58<3TW6bo{{KLp65ejWn0f*%?^etcejMDI)BQkvR~&w8_NHLF7O zZ`XrGzAg1*XkYsKHNX8z)fAcV5Y$r_8zHjSaYc`ghAaClb-4MJfi6s3)qH$tRx0qG z$E^6tp)odr08*?aeHv7>x&8J9h>v4XaRH^| zM0d?AdHn+?jldY+Fw^_IM`yg;%jnlft+!?^hJ_R^t%BQbozBM+Lsnf=kRZa0B*T{2 zP*xk~t9Mp}^i`#P0|y!zN%jhKppCbhRAD1xkSq1cTTLZ`^$eqr9hhe9m$$}FrW-6f zimf^uwTL*#y6)-jnHCVlJgI7U?gdBI4hSPjW*3L1)&KF^G22WJvqU)@zBk^=$W+H6 zXK10LI$ARAk1OT@eYIF*zc8d#>u_f?46!NbTixG?(}cVhC2M$7zF?JZO;d~OU{Ezd z(9kd}(SqKyrLtjyj^J>mWZ7}YwykB?sTl?Tju9ET&r#=8R}RlE>fTbkRsbZ0n?bQN z&v8AaHBRTQG)ElBQ80z8r+QYr&bSofiUc_l2PWh+6>{sf>ELSd(sU$^OM1`hDeIgx3GP+RNCG?)5wO98MO#~ z!EVgjm7R7dRjBAP?)bxIbIz<;8+*O|$Br4J5?&q-8n*cTO4X6`p)~G;b(NiQkf0iB zO=|GU7fk_sXio#I+6G&6qrIKrE%Z1nJizT(jh?CF$4&rFIZX#Rh z&gzYwk90^*z3)VLXu-}|wgCwo*3DPJ|DYSkL2#47$kvp&3pG|NK3z=qO8PlZm=Dp} zv}Il&5p*xKu&_`!Xz30yS%|8dxDUIXOcVS@Lc&nx1D+WG&w8<9sE^8kT~{@&`a$lW z-R>;eB6QdX4fJm9t>(fwVLyVVlubbi2O$*7t*oq+-pBa;>}+-Cl1kmgp7UACK>e*K z!8Uvv_oMJq3PyRy%2iml$DG9e(zrUE&e4=iAQARDU3aDIAfym?jcjY?ouz^Xu1}Mx zfr|k!X`7Vs^xIelZ-v@CzH7!uhU2w}3`r5`vU5me6Ez;pMvV!6ZQ6D`u1D^|)LI(_ zE@Mbw@)EL5j1W?BngO^Pfb1fgp zjv>BdYENt*s_=M!_`Q5!Pi;WoKYg#RU3;QE@qN=T@;|SuAMt!&Cu9Do838i_W(3R# zm=Q1|U`D`p~gpL<``ld7)9jqLq>>zixNF~=Bld10@jEX#DZ z=}b&aEOI-yD>E_8c|iaD_Yb_HIxVOT|J!6Gv)@X^%*e`C*TRrVLD%Ypv6+?eQN5)$ zh8C7b%}fRPg!wkCU3$dI>V%~jKflS>zrbf^ahSg*yYDLAg!#ly4NE2_%^mdLZ}WV% zZDIP2iAiqz7FD~Tj=C+sF)jVedHr3dmgK2Yb#pE`OA03AmwxzKYN0*thnMij4-2NO z{rOepKlcCpV%Us?pI_X$=n$^thp$Tl4}SmQB_^h&hR#!edU10B)1N=R=q( zk4y3kl>E3Nzd*@PEAk7J{In#$K*>)_@(YywxFo+o$&V}Y3zYn{BEOiDpO)koDEVng zelaCKF3B%Y^5cs9VoH8m5vE_D(uKLG_N zmKOf4zFqY{L2B(=Wn@$z9JqU|uO;r@E4#IE`I1VHJwjbys@)ge>=X6B=}FuFqiW;) zm)Gd{dpqN)#msMlyw41b^c6^RhjcqQDSlj4$IQI|>v~E3kN=Yq=+~=^nESt59U%d& zFEM$@GTElH#$VjlwCJkP8lz`dSsR@u9OliRpI6|!M*nSrZz6vDTijEs!ct5>^l zDO}y2Z1n8r$=-J_;%_gxh+;|vzFb~UX}lNso$4?SKPcR3_P=WmbSoS*$INai;Bx+JGz zh_3*?FLtlJ^+mzrBG(-HtL@$e9HzItT;O-)&D*!Re^29Fn7`Oke(}jSmtS1p@{dpF z)XneinAVFKm0Z($-EL%PC}V1B`sUrc9gdEU8%^s(*+fhiA2iFf?nr6A_#(?;q)OjY zl5UJOYao31)jzYE@Q#rmtdo5IM1dHScG{3ZDcBK7l1q$lise0;(jChF?yMEk4sn~MZX z+;Jz|og-5&iAAwF`^E^H)I1&^>0Xl0VXOJ6tzylw^2p1Hd=mopgCE{EW;@jk#HvIn zT6NWJ>aR7+eqV8WpH+8*q=kh=X+x;Qcb}j4-RFOJ${q4pW`Wkc&>bEHR=!TPCim<0 zo?qW${&=>mLE3aC)}>`r5**{qoAbq;3VC8h8+ zhUNd8`e)O|{B`Tr zt$?^>?YUg?F4rVa=P`2!{nh*KS)mmNQ}pGWoSc*<*EM|cFGWunUygbbMg1} z+!qOQiCMi*P&%$Eg6Q`c8)%f?;mOI-*H*FnDd%?!X~6Wwz`1#jdYtB>X&hF2-8m)G zG%G7&4~jSrq&Mc`8Wt)93ddgJ+q7vDmihE!&eI*UE45FX;4g}qX7;_b>#sf>dgsoa zt^G{b+t{S+2f4Je@$4v&?FV0<-(XyoF!-^MztDQ@tg4Tt0u@fq8E8*SVTy4RP)8U}MDwbd~ewlF`u6NPT%#z;IX4skW$| zI?ECnb#?XBIEBaGf-U;{eU-Fk%^ElFJyNN4|EyA!8hSJMF3_wgkKHUL;o+S2BwT#1 z${tBp9mgTVPu-2uNy(Wj|Ksf8=jXSeKF!+AjXz5?Trrp}K70IBN7hK!+XMOqK0M~V z&qN|3bx)N?sc;{-XS^uAEA9BFu6l7(>^}eXx?AP1ZsZy6?_?J`=F>f~lw*^HfyAfh z+gNK}e7nSg46iSHaOvNLeCcbiIgJm6^?I-gx#E)6aXNf@QFZKgta4R{bZ5e&nY3^V z#chl`Qd_PatzF>U*7;z{EcX|oviX#ouq1LiA_h+`?a#75IX%!YPf@&co~LC0etG}( z%WSH2&s%7nt~T_`^;)%?H^%K-i25%28lnlmQG%^I@rXsP#kS#O%itUDJxxgCin_O1 zY8?jZ^-(L#E46H8w6(SKPBHCuD38$)G|lMAOF_hWXBoAY-r8XG{`tIn1u=@jV!06^ zj%?>SPaP?(#3q_$-V$u9{$%{SU_1OZ*dCl39c=bAM>*mOZlvT|9;x&q&APqZcb76A zY!EX12AhtmBaK-O;q6R&6*TR8ayJ}$;2hTLDe-aV^jZV$%u{_$0f(<$(6X%;YCYbS z(ObM9MV70x-TjN9S#{bspc9k7sU>Lcp9}cpd26FoBJ9TcDnlK|hIvfYq{KV#sMrs; zMwowkA$u6Pz2fn@`*m|z*f#Fu6+c=UMuDSTw>tS)WgN!}*^BbS`bmc#{*T^3i*OA_Faq%J+c42w*+a4!lS=&A8Y>cDL3OT9$;}6QHU7kyD#!?683$BcVwsi&pU#|W%fzx+k>%I z`YxUF23}qvn^%>n?_JQ3=sq>j72LFgH9{$L$D(T6r(UZz7#8>dnd~6&b7kGZ`Df3b zRZ>-*H#9UvnHB5iLdh28hn+**#(i{j)ZNOkckllF3u&_z1#J>UE?Y!f9Q9;>s)Zqi z)b#1oZvxc3h*rCO1R1sCPMKoj+bai+(u7sn4w~FmvuZ2nks9epK2vQVQ1OrwIWqQz z8{5uv6%}8T6raC#|6eG&JeA07fkvpK8}#$s2itDPH=*Ktoc(h_9`?Y^$jH?%ksj|J zt8fJhnHY)1Cni1onft;t+ZB{^ zuT}7Hagbf--AX+`p&men-207=?1fj>xxFp)lf~nkH*el{q`R9a3k0p+qZ(lQtpMZ- zYHd@~K6>=1{fCFsHdwrQVmC21px^ZP0s+mi_>zJGSJdx`k!y9fjSefs#l_Eh-1yjz zG@<+`7C6YY#zRoVqYoUdP92@pKIqy1%U z<;b}2ntgcyje1oHx*K$J&REz!dHVDM*7I>UD$@JivH_THdM5XMY(UY8(cWg;>=7-b>xi6}9xkG7k)chfWjp~^CDtTNY%-fV3joL(CAB5Wc<&qpTA{t4oSrWLik0it%>oP7Mbn#LvQ^kq2|%nqbzwgE|?cj&m+(@jfd)03qZ^cmTXs- z)jT{GutaMvN{`lmXg?DP3vpyrb8G(B)`ay^x{Yyim;Q z$k~~T*@TVv-!ZNh(CuoE&%T&kEOtAmU*S#8pZxO22Gt&&V`;v?eQkgDhc(fj=UCXe ze5np&r_Z||wV2u$1h)e3Nay{=E5~U~W-O7CxYy`7B05lKsUkJXyIgPhQ!7E)T)y;< zIoQc=2!(L_J(DXD$@dLV4n`2{Lx~Si?LRYD(dFH&ki-R`t)E`%&Vx4ByI%k27z4<5}5$nA!tb{%`mZxQjq{z!St%JJl z>LQ*}!$yI`>k%A?4;;^2eJj+>^wz0!lq3A`dS$7spv zjzqinH_r1KG_suRN^NP1OYhp{Ux-b5eu<+f*7)*AMFZ`mDGAHC#!Y`TmOA!yiSls0Z-4PS<+$F-Ls@e*V|y@E)+VZdN!sKI6CFs)#F|T*DQ-S zZLn@DpI&XNh5W)XW!g-6-qR1KE_keap5saqpDG{PPr1X%$s!?sYq-RjZcA)?7^Ea@ zjO?38$ZJh}hF2@^n>1fq2B z$UgBphk7^(RD(s;PFe&%MaKu53x&!J^fQOv1-?kP?FJTB9MV>MI6Bm;AD`Lp3e37z zpM$z3>LfDyYmT!fHQe&kW}`pm)S+z;-4i0kv&cnVQ*$wQ(21>UHIi29+5k(rEZcTY z)ovPZe2C*PPiqG%&*ik`zVg`0UIoDDD>K6d3x>xC>^$ExEw@s~Dfqh^`-ZbV?1|6# zxJx;8#YNHO=scC{!Y$H)pqlqM4XcD7-)?Sh9%NN2*Mm)jzDHv4)j#G~&F!e(HU0K| z75kbI%ks63z7@!zdf8+BZa~C_X}e;yGsP~7wM9}xB78Cy?V;V9%d$5$l8u;?Lmc`w zf&eZ9Gg_R|scS3p-@t6y*Iwy4d&Q3JfZl5|rugnd6k1qXuHfhA&o$xoG?3;)rdK?r zaM;J1wk?WK-kqam0$N{Mt*ZZiM}u4!@E1I+NCiF0xa69o3hs%0gWh&cn`*B)7e(j+0 z8f19$(VqN#?hyMq_}tDsiCCxO_;3ltn)!p@rfolZwDphVfkr1Mq8yO4lI2d17t#fb z)LW@2R|A`vezd&RHUt#nRUmYN$(M(&H(rcWkn<1tY# z)bwbY=d#K%juuWYiR1I zc3X67C?WN-6P1&CPKV>fgWt^2mgL?z&&h`7v%Lsy!U{wzJw1vN-r<4nCDhE*4v)+1 zuL=9(#o7aRR$r7Fem{t;9*WqI8#w4kT!!yH?M3@^9ISIX799?njU=7VH7gV)Nt-%K zIhK<{_1%sO7$5vHzF>f30^Li49B*Z+Wy?H~C<*o01HPXUGQt%CxqK~mXxtY%{_^pw zhyQpN7#%*Gzaf9|Tt7_!&C#|ICsS&AZ6KF4Aak2?DHcqDT02e}`yGkfm2c#LXEC z*DrNMJX>dVKUv}Fj;>Ea`*i1B(Y|sO5mA)gn`rROQLOSFUzpoW)Dh}ESA{MxW=}_Q z$(8ieYPNtE`M@i|sy632Y+Qg{#T)FJJqDkdIVLx}-q)^zt6jMM&}KAh3(Q}iUBG4i zi~=>SJ#I~C^5#1XmTOf|SI9!lsQ4Q|oL zi3P3Lr?bkp9x;dP@^SEesOQZ)cQ!^Dk^-J}+NV!V5T%eS-kz8PVWQ~e#OPetF^Cw8T)%0CmDq>#ac>jHi?j9^ao=~Nxw`zZ}c(*$mCBM_NqO)Y&=I4Ss@Kv=5G`V-7t3Dmr z@$Fu9_4(A}^BzRm3Y!=e4INcRfMyIdoMyJ9TyLEGO!(-LdDIXw`g=}I=E)WLM*ICW zdp3%n0&nF#lUihsAPVUHiFz_Qnm`TIfJA=KojcN%ZdQl5mNU#AZo5siJg?P+0qeYZ zkJ-&KW~X-~?LHfuN>~X^fuE|dN{M(q(DZ!7mV&?d@cYnaBw7UxPwFmc$JYv#`;JhvTGiZ|O@8+3WH^tyInTWCzo+G4Tx7~+qV zhd_tO*z~{aQAWbH-#{2LEJ-WPN^ZEh$^GN*xr#dRHa#GYBJ4jD zQ>_XXw{18S9u~He)9%&Z(`$Q?ZQPJ>xr)Y+{LPy#aPN?llglxt;sXS^!K@+UOf`p1 z)iPwdYhh{1W10?q6+QT?+`j7|)S#$}=X&s-2T>iCB|KJmS(Y<^v0Ji$E6)Tpj=mG2 z=sG7nd9M%h1iq3!RUK3rR2MJ%Yuzj-WNS5^;7BYX0Y)Jyn(^N9Ha{=YXAx!%&;@hkq2ff zdp=2yFjdX_My&;AVlS-v$&d(aK_Fl!N zrjOEH$S9BFmn?TLRjQL%btl+pd8sLq9D&um=kM zfnwIjcAYjTMZw@$;y9?AQwId0qVw)>Qc@BXiQWDv$(6iTjifk4{e1#r4AFV{kaHY+ zvJg*?(ONGAh>cAciyWrsxWwU;k|xSdB2s-Gny>lZ+C+mt`T?16uXpEWA-PkdP9r{frK zg)iVklN&!*5ebNiAVR{kikvpRa-K5Br~x!lN?Y`8+&?iEq{Dz!&-ugh&-rUz(B^L% zA0Nn;wdtQ2%f3LR9mr@^T?F<+4+v}1V_KzQ8B+ovK6w_S<<7$0qs&X--{pc>bXrC318POKY&fI76x|0Eto)79f~a@*J^m*bHB zw}#HRzd%qEg_i%U0&fT)mtd4;cSEL1-5Y5R+d6aRX@_J+^*75!PdkKGl^>0(Z>i+C zVMMTvD@YjAe*&PG-nzA$D+9ToS&%R%+tpF!Kw%R-)UAs^oN*$8pq^Kw2P2)d7CTtv zn&WUQ@fvzVAK$VUQAXx0-9D+$syd>KJoaqBf^Z-=l0Zkm}5+ZFE$VNXk&K$u3z?+-2t% zd!56XCXBBKdEdzHst#&{gP#=_E9!a|9PX$}WER94%MD$Mnjj5A20K2dq@GX)-TJO{ z5?_zKj0wPk+`^`27R2M-xy}328eU!oCeZCd4v^m#Xk80b(i|0W?Lha4AHNPKO1>vj z_l>Os^$3(^Y-u-dMKK;-8?-WsV31^>K{nlVHxJ29^4~)Ht5CYuyP5AdL0i6$K<(wU&A`NE6UPz9hvuHIA@8h*(OIO+Zq$ zhvGob^nhC7>KovmT#rkR-)BRp{$a_;CFm|ga)fpFs4%WvEw_!zv}w>o$As=8KBFsw#+`YL4fSYh9_euXWlC zqRC-M@VT66Xo~MOQJGxS|ovzpoR{sPH^|s_=f!V2*4T9brSaE6niW&WR@`@G%sZV@`k& z#DhxRjLD1NdF3nqe_#|Z@n{wDX>bmgZ(&h@pmzZP9$&7)%XI+{RdRE42{`!lS3zD; zNgFRAVN^FftsykTY5Zi4{P~$2rOyqlD+=D+f&j@DTU{ zwyZ`4Q9yu^3^dnB8(8}Zs%2FzB#GdJW+=34njR%;Ba6F;cctiiSEhnKx(Swm^|?!1 z8+dDX@-|!qdL#Kcyj_(yH8s`zt?$0P!yyjy36X`}1>4s_SPT!&LQ6$id5&3TpStFW zhf`mG7de7JCequSD0S#e6eKbxlMJBqgwa=#FrgawK%=x8;TVAm^bsO9caZ@+DTp@I zSU<0^j8(l7!X!@as%%*TGB&VvI;$Q?ah^u~2hP8}xNp|zk>!Ye=Yfr-c4$R(a`srK z*$WVvwEx3z8`P1MM4dZ{S#hcFu7;hP&(-9fq)WsgksyJ0@<1XslhD{(A}!qy;h6{% zN@C-K`O4B*xUuXQIAN|D+xGBubm1Jd5=0cG(S(9c> zZlptBpVww$XRZpHu)#92+do+Hv<(}Z+RWSg;uZaY;}qGh8rL+mvWad##yWq#QQFqC zJB?fvWC)f@0IpL{wVIcASKCzt4KYK;h&#(2w%AOyihG4J`PJ@Ea%F-oAOW9pQo2RtC+-P3$UqpCi~6RvlFv zpcvh!?{|rdhOWEL8UN001>c-wI~`ND!-Pfl8!+nU z4gQdg1xe4MAZ%hbB_aAe)bKL_pw)MvBfaIwjwmmXR=0dtbYXmK#H{=-sZG?Hu-3V> zT?Ye9?vT%X4>=2w(UjEHubeZg2oWsEt3|eLA{0c$8hSio)eA%dwgGb97y)wN`eWwe zh1<^0jD2qX<>{sger;IsbZd@A5GSu(P0>x4`@jGF?@RzTx1sTarx1TGeGXQ?qL32# znFhDlQqYmP2$mze(8|HFZl@)rQBA#fGmUCvGrWI0c}HMMc#ITtrD;dtw8K6MeyD*U zZw_moeCx}Al1;g8`kPRRs&k~OJ>EOhYv7@dvzA~pn0`PzIjEx(D5mv=F*U4;M>K^*n-*y!({4t$Pv!Y_qb6>MV-_?|tcsp^_e`VkV`Oq)B8& zhKateuF5UTdD0;0L=H35YLG2N&$zOyITf#F<%fJuX+f0aV_cL6DGcFzLO8LI9#r`5 z)mapJ9I%BnN#zB6Y|N{?$w=5M`K>|tum>#Ze&y6e{ro&cgK`~LFLKEhRZ~>7sC7x4 zFayZ*`gQbPw>Vy%WSCQgB<6RH%eRu~pKa%6L|wXzJDE4pjc#YUhFe<;%J&8I5vv$o z7CCS>Zj2reQc^YpTyR6rE=i0YNYo`w;2As`C&mZfa-O>ju`C4!5#{o)3c-SJi#py4 zC!T_sxZY!zdBRbP?^PQGVzc9cx&|88WRc6$HSiMIQFi$wvnu-d`rf*s@HQFl7t#rp zOD3GB%ydDrK9dSyEKadXAgv6FQOfAYLhphGi^6q1xU^d-N_{QvUfQUG1k`hpBNm}1 zYt=bTjLC(#EniNzL*nXJY_qxVTTyu+Z35EmMds;&uq3pqIx%3vb3FuZJB;+pu{;d` zlw_+JErnNZt`Ujcpi$7VDVw0z+U2i-=L+3x1&axjo@|V{A-b! z%buUJdJl<*5ia2=a&HCU>aqhrG51~D%UE_IugjtRLr6{wlL~iRz<&{~&(rlBj?2)2 z5)R?=%|tKl#r8b!+@??i^(|YrkWSST*f`1+HHwhB-1Mb)+uQeDj0%D^G~Q@jwLYqY z>ijY3dEr3f%z`2IALZ6+rEO#}884yEZTFS6a{jx+rPa3OU*rfyV$$sbWI!`h&z6Ie#!YYQ$U|J))Y4 zOV%c02*SG(IP5dk=t&}O%%0p;swUsMt5JE_k%LaY3y?F6fIkl+#A#P{Lv_fHbB$cx z+?v5Q%b~^U#Y5!wZAT(R-O@n^#4HFd<>--*v#1+_QoE`s6He4b^tc;07GzKh2xi5< z{8AuT3nEztmw4ViTE=y5CdbusZQ}P4q3rAx$r8M^4*BQEhXH#+5&{wsttdoVI@cpw z&L#O-B;NRI|URR(4o_2T{O!cqi}A4u}_{?1QJ;J z{Gk9qp*(OvR~eK(`GA0<_Q1Ea%U^EbdQ|8|F<#ww!1}Hz0m6NTyno}WSN8QqU`wFE zw~gs_AWC7h%Qky=3RN1aX0S-@?Yj0>X;HVhu<@%0e-m|cCKW&>x35wslYEln0#Ww4CJa>;z`&Ez)JAfzbVXHahHFfsJR|V5=W2tENag8 z^dwtpR0p{WLBy5)A^3F0(_uftVw7ge8`why8f;hEfO3&V<2&aak-()Rhb>>bD+{gc z6=MB-?M%Y9-}XbyHq;?~xTJ`eRvo`6-nFxkDe@!lx^*7tOvLc#l>LaC9kDOsb+Vrn zD~<|y6FY>8l&o}mWjhQ(7l6#UiiD5@W=fBK@Ue;NBEQ*sL4cBt8;AA~XQQ0@Ctu1^ zK#lZy_vNz5tTrj%F(*_0U~I?OS~PY9@4O4fhf16-0?9ysIH~q>M6pGOgRRv|haUbjZC~_nbCI1qz#`;g z`#?*w3avZ}L(5{gW|=KvIU$3n4@)AM6E%V91z+H3~-2k=h7O|2fSs=#ezGL8^ zU@K+dpcKt^?$9K9YJC)Nm^O~F{=p3oL%U+$P?C#|KGkO1OrJym<1x=1G`=I$u;TwdbzG7#NdCd z%*pUoc@PE7+MsKxg76_5Wsau6w#~dX_<&30&dDH2L@#c7=kGr2;VE?MP0*@q-&m8X zL|spgCe#}J$1by#Ku=k8GyHns2SV11V5t_0o;Z|RxhG7Z(bjlB6gW+br>o=)v7N^U z(IcwVMGn^nm8>T25K?B;?}4k#fO2}k|8N(PxOeo(q=AX)`((nt7mXds;nbDM>GVQ| z$w!4_Lq@cj9vEAT7W^-21%j+!Y9!+EOh0G~eTadAsc&(V!;NM>i;{v%USNUc` zz6eDvV!b!-r;$ZQ7C0P#gvPWM(cYzhE<|L7?;Ez6hTiNq3Qd+d|H+VLO zESIDgx}a+aiw^)J)`1l#)aef&{PfzfM<6=q!%L^r0X(z8adgn9qbGY}BwGnQSy&Pk zN1_E25g-aRmhip3fQV-YTZ31@eb8HcXesPA1iGc}Y~+kQwBs;}G6D!&m}tGn;CC0$jvzq5DmL1i z4Gn2ErKg30M)EnvsQh|h)plxp@k)O03f#HXk58+KR$a`x&8Iz$h9~gdJOcdgi#G|t z(!v{42F8AFrKY63q6sRR1;M2Jy_of8UKY7@%IeCp=NNYTa^^MnVUIVkq)EwD$%ph&@d#255j9_Z^5NWE+9=A?C7mIzCOKvW>fAz{C zw#?l0<`PAaRr||zG=)RbXC*mLVe89JF>h`{T64n&<&XMXl+^^iZ4a6kGW~iL09HuS za#mrBF}m+ZGsV{;f2TH#`tYbQwgie1Kh3DcnNrrlXT)k&Cg=iOdti!bJt;-pMdWws zfr&wO`(xo+*3v(jZ&KnW^*w)wn1O4{Onyp5qBkv zgqdS+peXifD3S_$2EUUjB$ytASRV0iB*EEZbbxH}7JYo4z-3}x*r#VwLEGEm2cgh< zS?>*OnrvL~7(Q@)IjfU=i{{7M~lJ3?YN;PtEW+x>7pR zJnTh6C^?7}PO?ot#DwZ6yr2>bt-jPH=jb=&D{II+osTAt*B}!u=yohEB~=3EL>9o3 zA&aa_qdFwZqK$Qjb5Eh6of=e7q)y&1sg+x7^?VEQhi2t{#9%`d+du>+f`F_6`(f86 zO$0=FgvS9(dibQ_mEUDA#l|_ajh_r;U}@t%WL6qt3%mfu#bN{-2`egFbb=(h$j&Ns zk88jYR{MB1!^=8XcCq>P(rpPZ84wlE6YI8)Ty!5le!K~T099oo%!n-^Tj=YYv+Q@r z$pea7%~L5PXjJ4vc{<6kaE?(tM70zsNFvd-=g8WR#+wxxfH@hY$O&>1uqK(QrF07{ z3y7DSJ+bhZj~u-vfReyQP#hfXN#nZ}>ZIWS$dcC;)B^dJbFsJefo9)oi!Y~+ium0s=mx@VrzW3c zRo%*Q{;n@Ob8yzzfflT8J`7RGPuth+PgqJFz;DllV`vQZ)^0xqZEiG=#Qu{lx*1Lq zzJWdvw{G~kiG`=rrq(cTJy3?M_c$k_KzY-6Oga40{5mN|gx-AE9~%K1aPu^4d{9EgKD#2ySM}gQls55?~fEcIchN zw@>?|;iryjL0wb8vM-F&C5PKN4*A2u@Ila63s(^zM7_!NPra3GW>`36ph#+IAy`~2xzrPQ}S-@plo=* z&SJN6HI^9z_>3aA_Q&!LaI~vqB{9hKr&j@q4jXK{>gI{m zj2ago_OWUMZ%?y=Gw~DoeUog7GjD`N=FQ1JFkPQsgSy*H9SLSj9~098_d?;3kUS6e zd}PG=G{Z`cDISiC-na~E%E>ECf|5^XujNR0wi6k6%@OnwhSK9VuyrW;=Oa&F>p5+| z4Wu1LlM%`%s&Ev{oElF`2vC7gB81@{E$-KZu z^6Bg;-OZm1{ILiOrq-_hLyzclNrZtH#WEQM5^Fr+Rv(nvx{^t0_^|g>Pxkkflb>aN3N8f1zcJLZp{E zkDP!e5B)6XP z(xc1B+dw}GMHW9={RQMjhsGosjNjfoxpYjz1!SY4dmt&HhX%HhyLk=j!AR2BUWj&&!3p&dPw)+wWD6=w9vL0Mop|tRrKuh&d^0)!p82o{QgA35 ztD~)CR6<>X~5?3tKty_DxKL%&JfCxZZL=s`hBZogPo6oPk7^>Gr6h;~dOHquL zzDOyOe04N!MGk8i;nkm{9W+jqAuX{VT`@^ZH^DNqXTC&x+XUaZqCdk-ilJn~NAHBg zAd@1%Luep0#~7Q9fuPthn_=e}ZPBQ9#~_PKX1i**-&EYk#sl|FIT*B^WN(omZ7ZJ2 zVVWU_(7;`UA-&aq%;MCHyL&sm&1Q!N3|^#I(10*eAdDPm8I68tF_I@E@sqr0*}3t^^QCyR zxfJ!JI}<9}KGNSQI5scx94tX_9A&u>6Hf{?J*7>S?;gWdrALP~K%AquqA^5rmV;h+ zfo_1hISK@NTU@pqF$MHJ=(GUGT?y4!!R=5#p&JLoDs_`S15we@&5LfKbEjB<8Q5AL zIF_>KuAx6YG-h7{^KpW1 zmf6iDnrwVIG;#wLnxf6@OjV=ggxdQXF}y0_@MUqr^m+Mu`G z0c!03)7!UKB{qvWN#6m2Tm2?s?xBAh$!&(}PXkOdHo_|vscGM+I`#&h zEWl{S0a57TNx489YM3^kX|K#Tahg74uOd1z;;-pI9#Bp>32X4`q2#@A6v960O7bN@ zzaGdO!j;tf!pJQP4opyMHdEx*Z`~EW+v@X#LkafbF9yVsck7Zlk`YbEJf~iuhKP!xfzi)xY^2q$-MfiPBEvcP8fiAQ%$Lb|SMk3>)C)pV1(1y;#(lBlz{5xD}Q6+P5dF{L>3`;>%pRM zPKO%il~GV4Nsyz-9ULRVb}<26V0I&%X#&a9_hbCB{W#93$cmNey4N?C z7kkNpXmoRWguO9*q_L9gqJq2QvD%_ctZ)ty0We*uvVbQ3Hn$G4xxy#%PNoQgT{LV~7ALYpPf?sbw zPBB6Ea3)UwmkBQMOK1f^HYdIGhoU>C8|5q-N-tz4wDD18D+?ag5LNFoo zR`*x=r`7m>tfXf@N0$w!`hk{%7%Uagn#IIRJj2V80WF*uMK;zlRwT#hShjC0FkRpS zrvqA=b1dh_7Fn^(*`IiG{jS}Gjy-lZHtOXXzP^9&2&l9uthu~Tkh@ICc_k6$a0RiF z$7=F?BIKFvbO>Tgi!5f~Ly02;VgID0q{NDGj9>JkZ<;NA9&f-VH}$+N_uUJuSXSS! zTNRD8poFcmHo*-L*+4oOrjRax4d5rMIS!FjFh5Rj z`u$`-{h3KQ26dBc41HM>n2vWXQxsq4aJFhKvEw-OBav~^Bh*IrOhqJeI;G*uQ2zGq zIB4iGv0`B4FOr!EB;lH9VS3j<=6~M{({;8*BBq*kR%Hr3utZ?!Uv5`FF*@1feepe@ z|Aa7T^jAcX#jYTDJn8U)TL#kfM$tckX6FKIM-BK??l#H{BkJGatVP6l zLX)71*+k|o*h@%eqk}m%U{sPcvCap?7zF_MNSZ?oI9a*qDvy zOONRSTKdWN0J*?iGSHg_QFSvm?uylro}ahK^J1YDOX+(M<@3kU+M-_w;l(Iy}ZHmY0@oYXU>-j;YO+6;rM!9mD$g_>O&)$G>yEx$Otg zH}$Tm>R*gKO7Yi9ST3=l3Xrf8c1{s4Dn&Xrg686QGh~6^Zt7$^ zGG-=8ZY7O|%xtV#j9G<>eKyokfB*F~l@6rD0hqh}+aU^&MzQKqydxig%O7C0$qY-U zL-5qo?>V>q$gD;F4ky)Ik-8Jxa_UGoD3R!u$L0#VQ6Wu~QOVs8{0*+Z-}Weeyp-j~Pd!9|Mw8Y+y< zbs2F!pV$Tn?yE>8#sKfWTUm{_A6XuNR7q;jzTal@%k?+5Y!W^5{V5^+d#fR~+Abg0G{Fkwj+SU64WE$5i*`DCiqY;HSLkYy=+~v$c5_bSv^zxe!I_6qWHQi@F#J1b znCH@PW1?|&QdrzGoZJ?Bzd?*n`GUu}FuP$66BaR3fcgu$nfgOg-g3T+i6$^3)$THa8avJzfSOk3oJzN%}d$=;6 zi8=k1e_qK)39dQxuJ6w@*;+1Zm70H84gz9hF~*yEV9ns&pm{5!&Mz<=b(DSaJJ>sB zk@j~bYpdLQY&nwW+wI9{umEdmdbYEV{An;z%%uY^RNusB2$|N2(v&xOZAomp92}ha zaFo&jxdhI3hU`Q)L@Ee-5d{*wm_J7~rQ<_i!*jb~bi>K!~z5x9)m_%l9i#pb#) zGECyqgWPDhj8jS{Z3OKO6O)2eww*nmhdHh!4@HKKn^;$QjZRg@7%)Gb=s-@5 zlWVwDIYLyDXdlsmL(nt%$Z%ny69WB|Y-2MPi>_A`eSL-|&$bl4p#;DQ!IPi^4eso3 z(coGH!!&ItIzFix{$6*)3s;5_jt(%dP}^rx?#R9-AX|kH6T(8Hdqic-_7jR)6Asa~#Am4)5sf8T6mG8kd~HJi{k zmzI-h5+?xdfVYySBVwPw-Zz_ZdK54VH|<)d(dH#m{E9?!pLkO2^EEx@zK%|I7vhr`+7Dl$qf7~^qh{&cG{)nV?n~Ppy7Br=h1cD)ia<4a_K#2 z9)my@VFeFPx{Mn;NUVc$pHHzxiGzOE`(<4n9rsNq(b9++{T_Y5sQ!~mN`~a8#S`M9 z6YX~Oo+LGq9AAnOJ&!GBWxEdMzX~JYwGAu-Dezja84dbiNlrGh%F%@6^d*isMG__y zhCdCt&jSu(^}%5jH?<#%+vXZH?`v&vnje`(%90w9@XC_*$ef0dJlBYf78^G{bqCI( z)Bp4#@m;_NMQ*LUw(+PkN^Zl-Y=Qny2$u0(sm zMIeo1lm8+|EO5VSRa$RLi6`x7X7Z2AV+wdc^YqnZ+!p$o0i2uaAKd~^{a_w+s4;Q3-;IMcgw9}UZy|Qd`7Me3^ZvRG0e;6EYHPX22Lq?F^qhe7#WBP3K&uVk^&E%zpp zT5&=Iv`^H6b8d4|)#c^gF}fLzNuL5^VR4fZ6oc~XTc*HQR&Z22Ce?8)ZTa%$pWvnt zvx=)WehCBaM#F-6=Zo*knmQ>=o5fiUC*e&1RB`JPoU(Nb+o~1WO0jnKk|psFq>mt< z&D^y8T*qz&g$iu>P^6>cqxnx(;xq|IWpE5+w2tUZ$TJqcjYr5PdFsw1>2wVK$KmHS zy3@jxm6gl098Sk-B&8ZFo)ti&oemKw%yAC6-NbsPz(}%?d9O*bLrA`APP_^xZxe{3vU2!Y%&>_e%oIO zDPTU*eN~if)OZ_C$Rc7vrC%%6BA4gnJ)CvQ>#nUWl z9BsWHA8uVUidOF^ROOigLIzL%GHJ}JUzW_c4-dcee~U7cchfbiwY8PbEGRNce%3IZ zl{eC)_FlA3b{4OCOyu+Dd#C>AKdYB6-HcC1$8iN`w%%eFF@29Cu@J$RW-gJ60ea-& z;dweWK5yurakXA@1ab-A&L&s`>XlKOtcE`wO&`w8%&bVz-G-T#-Ec4}ZQkq*=Lb0* zTQM$LljV?(Kk8c+gX^ofDf1^X${uJ@MO}sK4HEa4XP&mjD<#MF1PB<;n?3u+rAvQ- z?2{iKA5S~g7p7K?PU18}%l z(LNm$sv7WqUamNW-vKQbhbKb!jwz7Q-m$xfOE~E=ZN}U`;Ml46n11!68U430^qw8n z$tL~yhY_OStvXf~?g6_@+%8|Q2!g+&tW&}!??zlouGx=GjpYwaaVC$g@gB7MSC)N#b*FhZ`gxE~1o zwrZ5Jt)$eU2TZq1TB{O6;UO|lw|%1u#qkB|j!FN}@bEDh%s_}-D)8esyIZ1{cm%lX z>U!P(;dYHg1z`n3kGW4()o1IrZGyq|+Q8fuaHQCj9B3&H7P5N37Y}Byw)RV>%rV3< zjQL z0gDYK+q}-1ifp+9uAK^W6h7CkU3;5>u-UAg``ed)X=5_eF#hMf8~HjvLCWsKA!I%U z{B@0n*DWm(=pv)hQe53+84hDp461!iU!9A~e`U6BAMH$h|Nh9MDLAZPwyV2)k%jh( zk%4ZJnDb}P^5|sQD^_>%ftYxSn=$FfnH;ic#QuJ^La%h9VJ`N%3TV7fIHsi)Yb?^y zn0fksp68NyjPw_F)ddLQ;iYr7*QF+->pEt4K;4fsDbP{v>!WiSU=`q@NJ zTz`G}ZhK|?ZQJgKnl$U%@?0w6A8XrT#m#C@MCbh;DMt+p)p=D3XBnZzJ>6Co;R%?j zl>7Mc{?2+NXPfSZvHL6h`^8Rv48wW(x_3DjE{sIq%f{J!M>b0N*2WXBHdCNvPmHJI z6eiu?!^`lnUpY3v{%RQhR}5ry!*6*`Rs%I6c9EcwLHl5Z^w8koY5@Uds_{&%w$y+=@Yw z7=k-|%+UtEDP^?5_eb`Y>)=>sl>Aliz>shmA zWr7bqknQLYj`vmDxzhz)UKEZSs7^4$`88<{BQ{rTc9vq0=@TfXs*cmh7k5!9<(Fg+ zb9+CfjBpf3LZQ3&N6I&GG^saRJKqiC$|oP`n|gi1);^B%qH)TzudDtb__TdDww|3a za?h+W4ru1^DMxh(4ZFl`yY7q(R_Guz9YrI`tC^CZ+n9OLv|1l2TDZk%0PEz-r*!~+ zu2RfJ#bG*9+=?jGOMyN2cYeRblixd+jbTVNY5!XFcC3ouQv-{dajLORauzj3R)W3o3Cxqh zQO8rK(tvRFlFbn2ibEN(nyE>kcOrhDI$hMt)bzeh=Yh_;)Ut#KY!cc9_6`oo84*a~ zh?(GLi*OF_Ib{`<PinHwX~|;qwCV#UXI~VgP9D)65!brCS1EYs3 zFRv9XM%!evf7Xl{4?elj?6|WM(Ey63UI%NDi+U z2B5fu3?N@3G#v2wTqswp_kw&a9k4oNIIg9gXj%zxYnVHSE%&FX;aOiJL zvc+tgELtNI>Ju`m5F zmVvq;|7-+HM#qpmx&}Mu)5b<=x}$qil@zx?Ul_^6KMq3~w94|;gWu4!Ke=&w0!Zy7 zC9s*n)u4C-?3kpmFc~T=3c4umy%uEOc;wK2X~)r&N4Dc? z{}*fT0aoR?b?s^rw<(s`q9|aAy+#lf0X4B;#X=DTRBR}As(_RU36AeUcz^JRFZk54$w_SH5I_o z+O;bE&VEB4**;ItC2lS{y?fs%3tM0`_S5aIeKk2mPUlO?89%1!l=Da^=JiIawrtr# zChYrFa^9vY<gY%F>$-tZ9^& zi4`;C!I;E0z5bFV(1eX8sjcUr7&>iFISVqg=*`==*59|!&pfxp(*m%eL@#Sy;q2`p zn@k>D-mt_osV)}yY(>O3-MSTCxW-2bSs0TYmd#N(Bk5i+QGEb+*UAMKR{I3aP4B{< zo%2PN5=hPEljvD=t}+sjBAeK8jYX2@Sh$d#Oq)5=?o}djB?zFo&C&Os0}TU>Lx=?# zl{)2Tc|==w?V3kfN7>*N;3FUCyvL{iGvZ7B{MXBWkjeRz&)Oa4pbMfL?5e(e`}5pf z^Mive-&R&eWbETXNi1a~m~bg74M!%?9b{n8)qverZ12)QP6MqlbEq7-2xMc47Xm zZQGLR@>jkRPSx1vX!X1wfBeyCRpO>3j)Mzsjjllv-0k?-SdAE0Q%JfX4d)!2mTi>3 z3m!!Yq`v!U4@$Bii)%0ndP$2`d0x{^EqW1})NDYx-=|1Y<{29wv`8YRRGQ=>q-5i;q9sFo2{9|Z}9qg5UPYh{9%i0bZGe8`7CZUZ()&@ z;aPvO(Iu2eb;l~p=+-^!G)wor?~|JI(_rzUj~VGa>*$zMa1ARF1O?8Ic$feUwMyC1G8>bNIh;lbtj)zN%i{qed^)V0eSZw-Im$*NX$MP6&z1q z1Qt6gxRH16ig)?x!BSQ7EFxHv>@*`3ik6HZOr*Q(I* zh0V-6k1b7!OPCQYTuG;w{kVKG3du2{*#{K3HE!=Va`lvT^o|ox@JtuZGjJ`{mU-UZ4`o&g@dTR3ZNJuLozj#ts1BnuNah;_Z--y-GW`-zV^2|T-xE{e! zNXqac$!}a{xc8nt5gT$R%LFZM*IaDCF1Xe^wLED%%(=}XE+>%%dN*~q9|`5<-ku>7 zhF7V6`$M8bZPgkF-LUD?rfoHML1$pJs_&lL%N9;}H*Qnt3)SY$n@fd60uJVEF3=1P+cq!0ouv4xsyp8sUfRCBl?x$*84E|D7TQrS zB=oe&>N$F}F~7<~hx5a3PS^aYdU_*+^Y=~|e$l#Db^H)rH!=uwm&s@S$N%_Ao7zpk z*1t2mYfb{pW73wl!?M^@d1N&^_Rtu+K*8yNIrXTH+8k~zH?U7#$X_-d|G5mFegpLO zfPF0>PirN$iG(Bj^dbgUL7dK@VREjORbcGPD_f}`5jVDW-l?ql{P>9R*}M1ee_8T* zGiw1Z3vn@$e4Jn24|u@y-b_F%z|%n3P6cYJ&JuLL`b*R2!X?_rlVSdZ$P`FOk~d_M z%hY_%>o`MVaih#bwSS@j9}-&wNNoG!PN&YNHta+Ssg#<_3F4j0iQr*ps=3?wT}{mu zt)C{)Q)Uu!GcNA#>XA|3weCGO&03ve&aUK%3Fme)34bMxS3#V50~v~Q(Q+ZCp<-}2 z7KXKDZ*c*8+otJk!}_!C-Mibpy8k@_nl!W|c3g`KKGt2w*q-4!hs>IlG&mvas$;mX z<4dKM{&vxT(HTVaadgASyP9tZJcFmYkP6wuA7TD zWTbuF(AIC2}m>wA!hoGH78NN;#htf0jJiEF2jF-Kkzw(Ro4%sLrDJfZf7yBHYGZPC`#J67KL6s2;lVjq9g{V_FY|YIZx)+7 zm;Exf@1Ch-E@>=@h^4pNn^RFfc%xm;ZIpjZ(nupioXawoYd<>HWxg!z zgnUGhci;ccb-J6mH@OYn%@X3`L}r!@p}cBi`ebiOpgW(k-X0%6#c^Yo#(_`4OU?G) zdV?d9!KrDTg6zOw@i!i=5zB_M%e~b)s8aF+4hX%3W@&HA%4YM)LQ-ifb%?E=pvM5h zHL*32p=NtOa}%&V)=6))dnM$zrcP{qe^MfdfrnsX>(U+GJf9>9+N1!v;3M0bzF_TR z)Jp=vStJ1za%IEe@K4}t*`=x|`eQi!+&T%6w+PQk|sne&g^l#npv?M7)7zl83qe5~=PNYD6^(UnQhstSQm3Pr^ zsyuGXr%j14oApSdDYf%bHJ{+rC5soUX(UlZh4~__3UD-eLo?vxB1gi-zTLZ@RSZsK zUW0}4LP|&d>S1P=?}ueySgG${;#o*Kdnb(0Vb!_M?Py}?zQHCBt#p_y1*)>%;`qJt zvap82#L#!!?fzxzw+eTXpJ6MWU#Lf%^{d4-uy>G!4Ub*xO5@Wp`)K8?R;#EBy*h9B zn~+&PhP=EiKWo`C2nfiikWhQ~k+x~eAD~|LaLL#obR&=GR;8@HaW)zP8>jvE!m9le zun4mJlq(y>`{q8rkiWS(iBd^N{nZuru|pkcQuX^@6>&O85Z1VvdkOrYfV`h z6oKdFjD)w<)q+GRLV%jau%JI;V>>A+txVsufB$K&^Wid|lAEhX7SaPf8xU*Xp+i>H zb4 z-nRrAg;=F0({YceEwcZdmmP=9e`J+#)Xy?wS)olaLX;ZxYbsaNzx{RsC$dISfO*tK zQ0bEU*DjAb;ir4@UgHcocJd}Qo%rM_;nX;fV3p#;?NrwhV-TZNTvI~D)nG|crtR$y zqQRLTu{vWe*n{1iz{U<8I-KAvUsd<+jgRYEs?Sbef4wMT?a0G|oKun&O{_aCI7C?w zFDN=%i#0uqCnVx?woANlj0ypyoB)FMQA++{U^2=&Z%S+pv#Kf?Vs^Rz%$HqUZmAMh zex0gJsF@FC@}or(xpOCq<*=jpdprcRY3)q$^D4~cyKiuqHBP$o=hORGk&bihgw8|F z*4Oyb_&Y;U_=v6SX-lJkxi_tfNrE178iFAV!pf_;fbTpLwW4i1cN%e&N5D}Y$Rh>7 zQSL{ossQQXp03U*8%zP49cIZ|!xYVI|I5Z?1jiSwUrdYjh0;EQ2CGem=MmvA4tRQ2 zE{w>6!vL~ays>|Rs0>cS$1bgLM6@(lfrBBFImNlMT;otOH(>#}{L6O7juat3RF@-M zU)UkQ#fuKenKHD0F@nt@Vb#ivvJQtvv4TPK7&mD25Ac_ifoszD>{A3m_p2W(^$(LI z4>UOQ8)(ogGIYI+b5yfCRJ2mV{8>yszNTR6K^oy10?aOTT5cq3fRyS|h6C)M4$;F& zqU6DYuP$g~5p)7V>Uey7{OazX$cSTVsio=aXrQ7N#O%iO=Lg05d{bGOg4QfNqNV0S z_`?c)9{!&y8m02G7o+(NI5On;e%DKzbz}fPh&>^J-3&pqvD;-6&iiB ztxwQ~(gLtLA;3W>)a%VoBpFY2f?^2odbe0|e>kXEU!K+Xxc0=Set((^2!QgwVw1nq zA_p*b2n^%k(%WANGtMnIGxyXqS@Qhx#aho4910_MfefzeNGl-+mY%`M9V)ks|g78$FsnuCTx{98GgRLp+YQkQ0o(LL9vH=-WxLa}SlgkebHaSiK3_;(u zO3GLcfy9cstFQ67!V2gh=lCh!VKo`#VTiJ7*JfW0Acqnjcs_?P6A%uadUBDOcMGKi z0$*=kizQPB7c@+6suxf!TzGu=$dO>ci`Tg1J^(dZvm7Uaa|>`io?)q5l2=$5gnPt% zww9W97{vA&q>6<|m=uy4PN%ul4jOe`8x?wojBs7@uLo!h{qbrU{k%_|jAV--2po>P z09$xWI7YPLvq2&&v1z@NnVI>r4pjOT;r#*j8X-^zh4ffuBq}Q_2eZ1ZK0&o0f58bTLzd+Sg06wzf@(ZO zEPBDQeEDG|vWVm0aZAV4*7xYy^Lwm_l7S~W@{8N3jClo>YX}}WZU5kv)kh$mLyXV; zAQ?S5*Wz^3GqZbM!_GHjM;kc|zi9d}995fJ_R}D&Kq4!frFMs-5|m=ilgo3w^GZvF zUVaTaXFy$N7;HTJeQoXC$IqeG&M6qbzn{OIT*u_l;a@v5TVSP z=>3!wnReSJdJ_o3H#M8?-50_Wee8Ii*A-FlH2m_*XG0cTHHNg=-`g`8aA}4X{?qkik z`*TU83<3=XaKT)g$?)OB$80S7P3TKpucGYPg>y+*!wSFt`t44%bg!`r)Mq2xQYyq| zvyD~(Map^y#XvfsV;C@Zk^oMSprT|aDeKd(pNz-3vg!SKW^D(tlEkW5&uxfMNfB22 z*eGjM)71_q%#XA_1vNW>55tIzc|z_{z`e~+8pi$utHu^eppeHgZZSJmbzblNSw!pS zpEVym^$t;yv0WlDjLLrrLg77vb3h1E;i@ch2q+Z zCC@D8HfZ%7)!TY#w$1u|JOoa2`VsF~sK@^CM+Lg(_@#Gh(q7_i=ua@d@p4YzdEHRE z@fc1p`AUXS`#6=LvJf`PKaZGTAS#!-Cx|iz^y>9T=qAwa$MBU2xtbS$`Q?`tY?Oh3 zv=?9Ya1cI`;2uEk@tSRGuhXwjpGkj)#Un?C+Q=3I=?VZ9ws%|3fg;gYW&-X>&N77r zmmu&OUeDpK>pa{sL)*7+&sgArhh?u)2d5z+439`gNkkaX=l|gv`L1^F-!21|G?4-* zb~z7rJ}H$=y0+Z1OSw(nvWlV~;#54EyI&3FEJi0ELN2j4L{ruiONjMkSNYno$Ld}X&3_(=hvu9TqdQuAeqfLo@ zGiXXpPVHI|77btG@#QyzUp>rHRaKRSnu~3#>Yst}-bZi4VLqb8zy395>1lNlr7;sZ zF}dYoh-VGws?{!J=X&+(0kEeCArJdEn!M}_0XP4elSbTGKAXF9(`EJb0@?w-VhF+l zkU_FF>(`-V@C7^}(JHZz^6zJGVx%!mF~YWBE+wm0eZtx^OD`I?(b2JhL+(B?EWAD` zP&jym3aMuoo@JZd!DDQAPn(01B2#U>&b}5U)cD9z+<&>x+!UlQ8 zE5e8&tK#UDLDbbwgbF(*i7*W&S1(I2s5~Rb@^vnJ@!v0ZWf8>&F1U6@axu~(D<0zG zNE?6Gx;MH|ng+|mHBHNbJwgZrGGvC_>(I}0a}KlSsX$U9R?U-rCMP~Yaq zk{(lRc^9YcGlcVMWub|ZrlaM3!8wqU`jlP8xt+Y3Q_e%W*#%<4#E*S~u6%_=f6 z=@pPTP=j1i1d)CE_Fe9@lV`#X@n^>uBGJ6Nq2)cV@QwowrpTF&!B_w=!3uZ}2}CHd z&TCv^EPyaloCv2_6ai8jOI3cpN|#6z0nvDSqBRIytoiLLDi=^!YyJ zgH?@VV@a4rGs@nX1zjhl>*6I#NNR@g5k!}9%-KAEd{@S(XJe~`^|S8$E4BehP0x=A zBE=Sk6k|nm%VKvq?qDNA$(-Tnp5|Runsb&AT0rl>BNix_@xIjwkbw-~k--Y5*ns^Z zQrevplwA8R>ej&}ZTQ+NlSNv<+HlZW5HepwMbr95>n-jvnZV0nDlTccoI+L)j{n-N zJ>QIw;g1}}_`K_58WKTG+-qk-Xo&VPT_3T4kTV)_w+CHHae8g27roOu83`tp3r94h-uFsMN^%sy<$c+)|8JszIWuPh!A^1fvlVtK zWq5geA4j4tJwVCS+nrQYLJ%GoAn3vZ3tgPInwr|)VGGBSX#~)kH4rX-8mkRkPaKdpv^ zPNOo3SeG-F`!o@pBU!{ye1yjL8hgjNxe}5Fl0>)FE^`M*JqtQ}hB`ptJr?^w*a3%_ zjz=|ZCX<6A_O6v9aFVWWAuCZn0<{mnx?$50sy4Ufbe>%yf$fbyoZ90pFO_CmmNhLOe!Igw*m-endV@9qx$xd-ebo$__q8W*^R+(6El4|5&Y5smI!Ylr_11oG$W^&wD5RvG6{A%`d^>F;=m`zo7}PtgR|Jgl~u2#!Ulhg&dP|40u>Px!_6=6sPjf?5lK60VY z4-F+d|N4FLeVPIby<+pqs|;Q|9q$n5-ujK}H$Dk3(fxu^|M*GWd)YqnTBKm8p6G@y zj0r`srN}*9YA>>QJECo*=T35GDkOV{i64I8pZ|Q)Qhxh*)%{eJ06&F*0V(h{zacv8 zR@IUUG|8J zWMi7gg^~=vEHRE0gW=G2-%ftTiJoyt#C{NiEShCq$8Nfe%4!fg?3SkloNelLy?(o3 z_LqdYlE$wXm8&bT|p$8LucK>}-?T0|MO-w<*nDe~mHx$Muy=kOHztXVdWIJD?oahMxs79ph4S zfuYDYNH?YuKlT6-OUZ=fgh?Tjsnt?JpJ|v{cbk-AeKvPNyjFD;wZlJtgZ#x4OIf7x zb|CLTB)U;r9+@1yl6`vUknrHF{0PIyV`$4Wj})N8MpU85^=JR)Zcz$`gfyjL#xjLk zu&MX&efw?}tL?kFltDUxvjTGo(=A^MIC1>M3Byn`O@}y}AjUuGqOTuOL>I?agcW-W zGDIQ?RrrXO4>+0Z@Hl{xc(0zME0(A81uJXn^|av@@cQ_jbB<5kGH#a{bf+#Sw;{}D zmdYUAE3^>F-AqsBO|D^XZjvo(DUmgMs3Yq-MYGr$PH2vzNqTf(p_!R4a2Bt9Z5l_}-5&8U*rb>}NKZM%NSh-czm&kU4 z_&Md6JHJ#^*aDE<4YOnb4SLz3#3C zT%aU1DnvmkeF3pm5i1`2z^ScPWI_THTlKMU(4$mwxslsLmgoy&2Nqp)|LCXxYV}?4 zFdzit51V({=W|~q&=_Sl&=v3^LknR8ezQ2FvuHc}ocL3>kO})Eskcc(>mxtLk zyK6N0@89|FKaKzJL?)9CTU-OT5TV&e=z95~MIYARbh9hAx5HH1w|`#I>Y{!6oxi&V z{`=jVP4WKFf>vu5s3(CLwG1 z(;mzBT=K5JTUR{X&;7TU`s%&EbUS}?W0cxA)?K!pv<;uMa=-wH7>AUZefO+AtZ&v` zke6oGT~5S|I#rv$=fHto-RANbd-y8~)W{pGtX75OX6v2)WvaD$*=JAxwZZa~No#|g zGyU!uTFB4#$bVj{7%sE^QqHw&m8ElUhKxEn^>M-Z(fmiv`2j)S%=FnqO0)iJn+L}q z-tXz=7WPwPPD9X(hZ-g``gT73gI-=A*Wn6<-s<5s&dDkZ7q%EX`_VEJe%!0PtTdx} zv-my~Ev*Wv1LU#j5{5{&L23&t23HAvN6|Rgzw|vNZ^`?O$Sp>$%Py*@ch_jXC4Yz8 zmcOP;%fH3pzzkPxDmB;Y2;U>-}8X)Dng{Y3$SVH!UYQwV^*z9YGbO&6*T=) zY~ty%u|D#0Y$35cz7No-MHc*gfl^ThJ%Z0F=#y>w6`!TZ>V`f8cQw*?yz5 zvzAVi0!n!k>ruJWNrijFmA!d0JUHrkGb0AECa9Wfnuj1llma`sx{hLN+SI9$g&U|p z1T%3Nw`iuju6xwCO zLr5sAsu~&WfBN(ooe68T-KAF!uspuN8RrLED4QtNw)FV{!ybw59yYM*V8>-A{4*B^ z%yv9*L++^i5i1e>&X{qv@Qu5Bvy`(7b6RhatLf45a(&bVo68n=D;}Rcv)yg;womx} zJhS~a!>6Tv7OR?+=C?awV!O_Ley~3{GkaR)u!icEE)5Maxg$Ca>$PV`My*lK9!pfJx`A9TaLOV-Go+#HwBK6~rXS3XtsFfCV~m>B_+1C7IufAt4{lec=n zOZ_oRPV~Fbkoz{o;C3HJFC8vrjLofgvmT9dJEmMd{m}Y77XWR$G_G4Uz2oNwW7o}! zRb-m+K@HWT`sO!g%j2^i^30Y zcfIIg`JKQ2K+Pe!>vp~0=vY@A<9~8l{PQ=}s!tXcy}$B5>+RNu6*&jgF1CiU;HdBK zsclS^{=O^Xw=G_qbuL5wX?g#n!`44&vC_-@_vXG`Q=k4Aqhl0c+&~x%Piw#I?KC@! zfpR-^z}Jq`zpEM|C-Cau89Ri=X#&B0;)1`odXzc`=i>wNiR z03?jonnnT0QH%!Jk(R-Ej#;Cyo%+w~{OjuvDnhSqSTkmZsdno0;zHKN<@2Bqk@%$Iu}( zj&rX2*_g)0?!(M`k7rwjZvRzz;PBydl$50Kt*G$Y)vwV_46PKe@BR>ViuG>rGo@5f zsX-M5l83a(xkkH0Y>j{cWk>)08|ACKPJO16HdLakh4-V32}$Mr zO~^Hq?``UJz59oCpL}Y1=hzx56qV@Y)O+xQflN|ulk>5d?|yZQVvVXn*nbyX3Oz|p z9UfaHWe-xPj5{BzWSiA>QXCe`7`XI3DO!9OMjhE3(xWloGDa zvj57c?^6o4F2tQZf|hviVja;DB3NkcELu@PmNT+H1_rp-dMeT+uLB2X^o@1GO=JY> z`PHS4&$sjwkXXn%BWGzPMv%Ak{pfu+7taA96!0=gCp$YEc*l6ZqQC7wF7EpH`R6-2 zq-G6R^t0{{KP^4?r2g{l-hJ{NCLCA(ZIR|}Ex#!J>)(sapW-?zJQZykJXcD@j|A*l zdxw}BNwQ^`Fem1Oq7c+I9Dckk{A4)GVN$V@y)TAx!nimZ$G-2P$f2U1u$6`hM~waZ{l#`#G!y`_R=88Uhx z>gDN^Cs$BiN`r&N!Z*)v(#v?TsnWJ&1K_9t3-B4K0F8o`W<}b>h2|fc(RUwr11YWG zm||oB+%IRfz=cQOh#KE@2t<&HaPr(@gMRTY_t6u|o=hrS@=K#UJN z%+l@&#HoApU(KQ>>gooWMOu64pwtq%sd)!QO7FE&;`rq(B6!52-#_t8QjuN;KlOFl zSIz923C}4s>f=l6hJ|OWj*t#t5nxDfz34IoL|(r>4BA8Fqbg%J8ACA)*FIS}Pc$-c zCBNs(pHpo9tnKa! zVj_*<_<(%7?dIZiXM6%!5PAgIyKkd`PF~{}b?2?Z<>0SX>3(&v=8y^b>v~6O{i|8$ zs4fn=H?veoTt?5NC?a}UMvMi`!g}D!P_G}+WQekw_-S8|jkm0*u2BjdW1wUJILaAR z>q8Tok^FLx{J;Q$r4-I0KrLem54X|Tx$Zz z1C#Clx;VwZBQxH2n`)(F7ZtUn+4^&8Zw^#$iJBl3KsMx^#-?}cY1qzOqx3L2S(>E9 z7Xf3jt*Cj108L+d@!=kiPkgE16|WD+fbKWY^98UhHt$xiTenVBZjdOuJzI|ZCv@ed z-@8vAnbYJ%V`>&a+F6Q_3O2d)xPlccuFzfio&pqd&!w4U283@iyoQElqB212>ZH}U z+~QU?0nRj~`4|6q(zd$$LyZZ$w5jZ7m zh>{F?dwJp)iZt@yH{QLd;G*$BgY2vUC94omsF@CigSSu{Kxke&C2#4d#3|FGqO@AL zHM5bD4o=D*LsE&~h}3^YoSkM?rY8zdGEojJ-IsxQZXdU0*m>yppIdW{{s z02`G@$bi;Vp&b`MCiRRx|n zKgq0m`m1I~lB}yn`HbpT-LdUko0)L@UY2l zAq?C*e}DalON-Z$wCL}z%d<0jHUGfy5i|RY9dEh1;+>P@<=&FqM~!9jqRKeeCt>~w zzvZ&2ENjDcX&IF5!biNU@#hV-9x0W~KVQ}|76^`P5DP(keP~~!qQ(!Sv2NDxUXu)H zwM3hhleOV!U-C|AinrK>Y8Tt;lhS|Hf8V+GeWtyV_E?8j&BlLYOh(S>1Ze5z+IS7x z1<62TUBh9xJ5j>HmU$oO9CKb^`yQ*_W&ZbqmFw%FEB%2uNd|KLE0C7S`InW%MC>r3 z&ro_AgY+8E)kMdrAx+c$`r7Z1M2=tTp~E83M=)!)H@=?SqC%wJq&Q>fl7&P)L0l(U ze2wcGwooCOUX}+9tupI(MLiAMjRWcNX@bl&|uIAhxZ@6@R ziro>)xpW4?K&V?e4f|!6?iAAjM+S5p6 zCf-B{9Qtm%>3vF6;3AW%q_JZa_tE<#%Z&<3PrP_YmV|kYRRVoM0sZ~yDj2MJ2x$}M zzND9zwbiYPW#NJun}<$xbTpPioA#^ajr`Nd=ugSO{9-!ygk(nJ7)W|;$V`CU6)|o` z#Qb?Q=~mEqpjo`=5doxiPUk=Kmr^HfmMvP-a}0+2lrE*;Uzh9a>oXwNA8R`+LYMR> zOG61f^lOI5dd0-JU3}ZkrQTGufrCqJ@+_wzhmOV*$pz(y@NRp(y%(p8h*HiTj<*_y zh4dCELTn%ydKHYn(P~S1P@=d(lNY3ef==v1R=esgn*_%UseF>wyCAU}5Q+<;fDFtrc=o`Ds*y-Be@L z*ks31?FZoSg938@$kpjjpPn?iL^qme0w!)G^yGSB)9{JY7RljXArP5SJ;N*q$P zdYG52M}{a3znmFD7f!HTmw9m}hcK+*wS6Jqx)2ZzmM*1LO58^p#?tW^LeacDU7Zo= zh$ga+IhO|#eFFHU5|bv39VKc$Zlo2ru+0=pzZ*9uN=x)yY098q!uu$)kf9mEm-VP_ zN}dRVeM;5^7B35F<1N|geS+J}6h#LQ9Ui73`cWcn1nQYbRF(-xH1#ozSm`XM13cg> zs8YR5iv%J^C(PYYYd$YtYGbQ5+Z0 z<(N$=GQ~**ku+|@k!S_9P}DRG=6<(`q>F;e-eJ3$1qaX8En9{cvC+b}J-nQXVn7_~ z*rivN=3tW{e?UWZd8uD-q@QPqcZ}Gue!LU9U1q229WGsqg`_VNL|1a;@H6~=F$=PM z|G5!I2)pYv3(urj)2-nuwdR48&2Gq8XS2iX=$;7DVJsce4j<0X@N0ZP$WPgI+#k(l z(fB7K|I9DTo!>a(PcOg#Em@^K)SK2`G+Xb61)apvr6UiosWJk+l7-1(X$af&tB1!x zi~zwvFp*9sLzkJ$S(i6VK+Z2N;I{9|EVzTSIK_r;)JVKUv2$1lbDs6s#z6A!7tIzq zcV(Hmqg#WDvsvC0H% zLpc^O``8b{Q4^eOk%HEl<3q@-*>4JYgvq3%=SAx8HQR-48qbYHTub^Kx~l?Nx>md2F;>^7~@s+J8B@Dn%*cQ7;*{Jh=u8pyNvA0#5ozetq_p;!UUEJb z0F5ck7*)ocYNmi}3blHrXt(LMu+Mj9vM41( z5(b0MZ+t-qOvY;)i6)p*-7isb<0-tEQRybF+|)1$(IgA2@C}s~Aczy`=d7AP_#3Iq z#LJ2;NOrS0!hyyEL47bj_YHXBwvqO(N)Vh^NI@<v^r|HDSj9tze!2Iy4UeBRb`Gog z5kWO#z}PeZjYV-NhK5S9lf{S+3Bd~gldx{cS+ve@S*27{1gR~mEBNmUyg ztL_Fopuw`7hL~y|CnKYEmPb_McXJ(Ywi7(fvkM}=y&u}lm;_37#>I^?H< z1>9^<(^hQ_$oj>ZtE-lM@UsmU+{O|Z#)u1C0}87%=(We-ZIsd=s2iWyfWuF{wtesV z((>;BoyZ-pS?X{opj$O$Q48N${RDr{%AyQ8zN}`UP8R)hl;z93>Yu~KhVu|XCLvnx zC@lo`E`1O5pYsE4=@(@c*cCrEWRAvrQy210fPi@Gm(H4`hkv)z^K05p&sk^(_u9it z8mr_K{_ody9hQA==(pY7Qm)vo0>Js!H12Ez29>&Ehf1G@eBjZmLkFEaefl-MIUYJ_ zbZzEzP20R{9pI4>;r$+4qAocYXL{e8>-- zdhac5AEXKGLH4xur154{)(*iS>B-|p2bfJ8EKm|%Sd%%DHTx#?(1YgT!NzE=E$S)f606<>xeYYGazV@#NhZw+yAH2b%bj!l zh@?TUn!4rg;&a9!y9kNcf~>6FhEnxHMj~pt7KdBTrf9+rBXl@82Yn_=Rbntm0g;&M zOAxf8e(=yC`*F(MG3tCo+yc?@({*&dvY`}5Ru;cSdN}pa;H0DhFB6Xj$qliBN*_;# zKaX?o{Iwu31<{PFViX&sgWT-9drPJhZYwD_x>hj-YW7#(3=UhBjSldFMeM&~ed$)t zi_CRQJik1l>tVNOE+v(z<9lBy+}E(^kbw3=sVe=_I=>VS-xTrD^)F6@L*>>U9Vr0h z_!7}l{qyCXj6U|3)ewwISmrhshmMp2m5p>3(cVwJLpRXJ{Gn2Vc8G8s9KV3p%;N<4 z73t*wvjtEcZ89upqSqZ^Uu5igPvYpNO`C#KFRjivj;o_*k+moP;^@h?$|5-@mlu<@ zSo<0VM022v*fYgf49rcnh_|8Wwv?(`;q=1Z{tlSaa+ko$vA&3P=0nsa+1{#6VO_Tt zMNTRN#L;3YAw{vxnmtf#lG??k?ZrwH1&p28baS@*{}<1@*q_x21uy4>7Qy^K;10dO zOWDg2fYQTrFW@AyuoqtDC1teZh>G5*(50s0!IE7%FUzjsQumwiB0-g0^K0sG6W?`e zJ{JJiV}G^>H#S3?3em9^wdQdUVdQA6ocF`;{M} z^WK%t(JduL#0Pw&LK$@8#E|MB(uRzp0d#+gqahdGS}tu48ZMP9;Ut`0hcEucB8j}> zXjOxxHJ!S4eO=}=xs9s!tNSjY75MMX50Pd(L3)zy+p3H)KzmjljNox4VKn2=LYzcN z{o>0GN}n~o_F>McV)R2xT~Jab_X>$VI@^OGN2>P_1-hhap^;^)j<(IGn3gojGx0*p zoS&sez1%_ArhmTl=7~*Kg!HeGs0^L4r<@+L%%a;kXJE~g!zx*$x?J>d`t~s-Zk%wK zaP8QULP2}vd@TYl`*-&!Kf0u{-Al@jhOQ@9D9(yXv${s?*V9op;4{?M<}_ZMpMyT^ zqJtnHit{*$hT@Vj7wyi*HLRqK6Un3AMfL1i(KW9J#n#}{__XY(5QQ)kvH8<(<^0@M zdGyo03PSaRV(OnC<^ODKcf|gxKMadVxh;~&O_ifB@tC9&D>mPQ#9YkM==5=Oc&9h- z9au!GLeb+ndC&}Q5<)ta&{GSa%_Wz0z2z~03{3UrVV=R(rdyQ*9yikdJK=5CFsYlk zqw_;NfBEGMkhNY0-R2Jd_cUr$>ttuMpY#}o#_M9!s+*^z(kbe}yU6pTyxsONZxefB zq%%$;W?nbm%W-Gykox_>aTTvq?uM-$w9se-mgmpZ8_UQR=7-R*GG@(Lb+NLuqWN;M zl=#W&*m)U;0Ip^AjJpZxcwxnZUY)SvvPuFJmWdgKwA9IlVatV7aWu{4FiWxzt=$pw z^K$Bn-JYH{u9Kq3{{hKt-kF^!{T(r3Lw4NzPE5@#_{zze4Aiba)4+;b$ARagI z1diFA``-0Im%FCw3J?xZ9bB~$z#Y0$N|g!L zS+g*ISY+%_W$CTnCXWO1VOTUrAP4$%YI?}v4eu06!my9D>mQI*%t8_y$xMjn$LKRR zlD0Y+BlEpOE#H)vhiB~R+-4C0r;tEyBpMsRp!L;%@$d-bkTNQS48OAe*-w~aVi9_* zyn$|u`&L6nMbq7^xn$#IAvT8I-YrcawY8=pT5ok8;;Bz3>65Uy)MzpFp>?^r^ZtGIwnklKT;wu53eS<0T|rNEja~H z*6?0ubgy1N;Gk%?QEYyjRV$Q6^3~s8X1ySyBxMuF@!N~frB@ySuot%~v9s$_U%Gxa zNVp8l6yON5(OzZSoi!r*3GWA0hE-Z2y{sOU(5zQ_>+)$^wOy2P$H#;oUF%m@W7Nic zZCRRTuJ;x_ZnsovxhH5}?!Uv1ZPeyyKJ`51i`4T{=Vv7i!b(b*5yy=iM~_IVrzQTD zRS0TVLa^=Y2H<(EWotpdKjv!Zw@;9J;BRS;2fX)RDs;#C#;5C~ZSWtj1LBh|rgj@o z#!bP&SYddjq&}IuE+)&5u(jc-PsfGRe*OIF0fvElv1%-HI%^)a+_>$RUs_LXW9kfg z@cOl*v z%Q--3QdKaW1=2Sz#%y?O3$QO?vH5_kyS|@FsbqsU=y|&lXK3*mdfTG#>2~#<-F_cC zIc~GH_ujo7{qNC(8ZMR&QBIy8awyF{arM0z)ewe#_p6Flbi(Rsd3T171k4$TS#HmA zL6|`>MMWXrd;kyo`$dnEQ+U0~N6N@hKC>Qcj-n<}m`JTI;mrk+p#gVJl1}vz^HNi}ogZl^9U|C1P5-ZGp$E0u zEIRkF4E)igm5>zRq@<(?cAY+myt-B?(|tE-WwYw3)2G`we0E9Hwao6DcG!iJ%&8_3}4OqUgkLvLk)3{X;kk&iiw9apVM-3RDl;m`XNjVnl??l}`$@UCZwEHL0RI2k zy7i#Y*a_R;2B*)7^lXViTR0-Q#Q4KC3wr|5qIl*_fvks zGZ0;1jjx+@8Hz=aP*Nap4>(aPLE$MX*0PS5?q5z}DNd1C<=C#DFCXWFFj&6&`|JOP zh2C{68L`lNx|Tkwk-?T#;|h*;_iKE~U0q|K6aEI>XG<#kp#r*#Uf_$B2^;jq2RKVtVaD32_i{4Yhi z$2fze{Fe&VKon8Fjbw^4hez{NE3t1W!YLZN4}D&FcCL=@A`zTLe+DSFV)~ zAH9H*yHz0s)zE3r$H3@6#0IXF^Y)#&h=`^!UhJn6OR_qX!oZk@qz*H`l2_4+VqPz2 zcPF|Q7~b#*D@`$VT$3L5Jqr)5{egsh2vS3kVTbRh$+P&FEqkQ!!X%5rLl=q}lB}IZ zFm^Ha=u96!`NESh*YcM$#fNgzO69Zo=qY^H$Jx9T`{AR$OMvS=D#^M0Ew5jLdCCq` zRZ%HE)MKbMEucCwtwBdP08Y7e>E#q{qPJ=@!@jXUKtjr6N=dN{@Glw1*3!?{`iYCD zU=QIee1-hP34z|~v6dhoiETx8(j)Dh%UnfnDW6L5TAwp@YEoE62N#_=FCS$No$_h3 zNZpEvi4MB*UrVMFkR|R(=0QXe4-l(Ta{99ztdZZDS52He8Cq=vIsqeReEE;Fd85AD zmrLh)0PU?H!1r|u;ma@$o{b|-vf2?kQ%9|R5;lJr{u6x1iwpJN88H#j(aCAxx4GGk ziE5*izdAn;E|O#Ww1T6Au1lj(HKg=elYP}BXH)r(lx!+E>3Mbn^FG>cRFde~83?f^ z!WH{wf7kA}*OV4;O9iZx1t4)GNHIu-M6ZcdNl{uX^LLFxZa~J#Z7_1CmUxEQ zoESZd)GNoO`lupvUPWUc$3^5c1{XB2l(cNmAB>AjMrAmtYM-NvK_iAn58(-Kq|bnr>;f zSMuD;-GyyngykiA#7j(eZP{}Bkv8oJu}<^n&p-Wg*-f#$*@ywm0>*`jlh?h&9biOO zKyMfoq9;hDjy`%ZXT_!7#QsMsqii}jxDLT)W|39W(4P>cy6*9X0C7l&%}*pb#47C0W0sVbHP$XVC+=U)?1l>VLf4wO_DYj8?%r z`Wym>ABF%Ws1ouU_Ic-DlV`&jvBQbsBg~5Z69{fg(*Rnkwq~y}L zPp$F)@FX`tv$6Y3TM#0dzVI(kcEjQnMJL26vUnk{I<%dR;zT_Z+h8(nE}~Ki6rKq-2N0daIvM?m5@vn`CAB`9j zr%L$FZdFAXKj4yf_hFqIg!g{@oK~ib%%5K2jGnb`Kf(6pUD6W11Lq}&#|^aOUt^Y{ zhYqzy3ixjEg;tGN?}t*+FD($0UJ*|$8+kaSN(|_MozFWj2OX7RSFyfgJNUk^;_Q=p z*YzWjkP9;zmJdgeiX7~fmy6Wucz3+?GHJBs0ZJ`j=6vE?2(A@G4-Xxa_o}Vf2R+s` z-P?P;K;V3IUMt`bMM0)-U&IJFD0qz`8E$tv9}}1ys|5OF|M=w#A739sQBra`tf>;Y zn9QP-CjdPj zR`q1XiwaA>TC4+vp|nV195csGKN76FD>d;C34vY&_VqwarpCDZUNT zj)-pH!i70A5}1hTEkQ_JI62RTc`BFKRM-BVfpTT_J0H2>5NHK>2H#EfB^k=pF4?a_ zD*KRd&qnMcT;a-v-9#DmthyS<16s4=21F@zMhk9PX3H1J8<@I<1bpup#!ajNyx>=9lCZ69J%K4QYU)? zcm=ZX`606yDkfbJcm-oybgdIf zMd?N1WEIf>E!-cdP4XgS4$9FvxLT1li=7Guz@mtCpygxMKXuP`tz9F<*MbEZjdt_# z%47qtA__ub6!_Ir1Nan}2ycq2$TGcLDjS}rk8g2q?g>6g0ExR%qY~q~L^32ClFayW zsQWz&&Up~YAE6sv};d$dz zoyVh(@U8$x>K5I(cL8(9fKzoZ+RM|7cmD725%1l;?Yj?+LbT|6x9|R&`bei~E~E9w zF1vHNcU$)(v4_t(H*;@IgT((GXT|7$J1ah^?dQs^12eTkbSIK$4jaSsOQ$lzl0B35 z`UwgJ(l(7`$CHgJ_wmkt$gDrh@KKDhkTiy)-Crq6Uo~>&!`?$Nfp{B=l-OXxC;!1C_1Ixmd}Y-sWXy zkFB;cgc!W?NqGS2hn(KcHD-(irJy#eWnkM>^&4L^kmvw!qY9Lwa!286IFcC)aTQ%UcJ^{ ze&(@)w=uIx(3k@PbYUFtB{Qhy=Ja}=(^Hd>vz^^dIzB`$V-6CA^sA0GOhdoL!MT{C zAmmM&dH#!HHV2o;e z6Gh!{$G3TyHVk^ec&SS}*#X)Xqm$a2ASQm!RDvBb^~LMHs2#cXNf&w6?5_PQO5oqk z0~t9T}7r{YvyR1+Y+vC zBZcd8K=919E7T5mbtnH=uVN%SZ<5>^)OYGsr7xZk6wPSnE0;NM zbJ1}PNj5>r>?)(USAKQ9CX{0<4(c%%!SmFNdqJUB2I?WJ+M58f5ZJl--wGAO2bYrv zIse@Ks#TW_Em9utV(R)k)=MY6W(K4k0YJUEXVzGymMXThK~o1#$~<+8yjF>sq2Jfl z<&WFd&v)bTxemAE<2NJHy|J+2?E&i+Q&A%JV$7s61QWwcmY-Z7Tg=Q*8y2NHpTgPT zor$42$?6ik%r~%eCzA@FDp6x*hiE*HAoCNw-MrUBZ$IIYDn7JDlDsuFy6&AsNH141 zyrFHh%dRh;@W(ep&AP~)<*!0#SQbqoUXECF-ThtMxQ2G)>I-J=@mRDkrJb&FJD#lb zulGU+$GlHi_ngYoMIza>L!an3<7}Qf@q2@2Mc35^IsZNFni4_WxHZh}{oRAwj@Dw$ z)JI*5S>)IdZU$5v*Qb1L09EuGG^lYzY-MuD>9y~De0(;@m~_URc5JItF+1{)v(XG{ z-nJ2A!d%O7wI)lK8$H=I!FR(gB^uvaJ)3vxJ^_6rau}mYu)EoWmmy<4R;+>!c`Cm9QVgB-E6kGZiG8&VKR`2dEZ14`t=RaD?om)0` ziEfRy!AZ*Do6Jp2k|2D?5+%)>H~0actXDUiW6!AgjU;TpER=ij1r3Y;@%rr#Out7q zlzEPuNWFkX_=z8q#?+O?=AtXxOx`nGJUAa_kbCXI1Tv1O_58P=+XR-a8W6JXtP51X zJY1u9t}six2A#dChEY@bM!LqNZ)?C&zoSQ0E@j!9GTy5XPIvnrW^hRFB3|B_&{n4- zP8kD^)=vnd+PFn?N*5OTI_zS{?z=LbU0R1ic^4$>cY2yH_I}yA9k|Qp32iic)AIJa zF`Xy!-)E)A(0U<}41s$6B-)1BmQgKU;BoQFo{c^{gjGsUd^%wy#4Yp%ohW z^3XdPdc9p=atnL)3e;`&_eKG&3uMPyRqWQwFAuF+OeQNsFK#h8WHExm;c&tI2M->c zR!4$N3mi}834~)W7}vA*3*7-;}vP8oXRb_^|L2YxJ3~J z)}6hX4?l{|YoBz490Bu#&yUz7ZsEAS30SVm zL}|TEbUMC4EpiKu&Jq%2auKin&c~=k6&so2sVgM)B5mw0-*7MXIhU-BD*nb(#okz% zFuEb#!gCS?&a}F0S>nTy04^2)ax<<$y{T>HmgsbF|Pum(fIRa+t*58IIk_y8QCW1g!7@1pPcFXHa}roi#XK-VgB_ozB`I^s%Iom zZnoD5_0VABupSn(P1RAIB=_3{tgS4rxi)XpWx5 zUF|D594QGr6l5gI;a3FuB38QO8gqYbG}w!h1xr7x8Ar!x(8n6f zMAYv$L1mv~0Fb0+tS((rr{g484?w-d#kzv=3f%o~kGyF4k_>Pw!KHE5lq{qMk~^|L zD94p{@7dFVAR*4>E7(6pk@>z4K+t96xg;GD*zQEPvY(AzaN`$BM_4)xc@Is`4h{}} zMemKWbJ4ls8_S-`_jesNB%1_!F;oNwlXZ=oS}y>Qaf@zv`SRstGMD3wjZyp_5+!lN zMx5A=2rx@>1+5!tE$p?srG{<|9@e2{@S3bsj(H;h*mAs5S5a& zZ_jytX6C*w&3*s*`#sNd9QPdeabMSE$oKpCyx*_oJkQtpy4`$hFQ|^G04ns#6vSonBXCZT2u2K@gB*nhqe@P#Y+_oADI4p8EEcA4ZwefaBL9yxEO zt$q;EG-oYe)Nof99P?ePSEG*25MNv|Y|(!G@;?n;Hm&;NgH%)Y=t;EXqi7xT1K*0D zzEQfy#N>JBmwtX?;F=w$3J)#fyMLr+;ny9ue)9c?8JoX+cFTYEF2Zlb|642nVd?Yd zuZQ}G{5O)$B8~sXBwNJtAI}Rv<3y79KL$*3rMC^qro!JgCFXxWE*`}HK3$%M|2+)C zo%sLLVdxqLq!dv1&e6S4b)t*@&@%DY!Tg#FlQm1`MK&p}op!`*|7eLZ_ATvh58NU$ zVjQIR>2_Bv+C8dVF+tvLgveU%?T82Iz7?1o5DK;lH1w7ep$T@vK+S5v z8PgOL5)yzR9_a;d%m0O#?p>jit0G~ys5&H}ROG~9Jm8m$s`j?) zFE5@0mrp6o&Kb?#acdEvGtKt{V5ZgpAPGh%ZuR@OpJxN5m;^0m#IqPg)GIxsK8$Lc ze#1_Ji7Wy%b}{!PBqoNS(ui*>P+o)2X9``B8}RN`Xcf?kNS*U}u_jl<=-3M%cDl3dG^znrd@BJZw9qDT$z@MQI{X&8~x|sDstjU9%?3nJR6t24&7ZE zyQ2~$b3D0Tqc5*ya=*zVR6*lZj~_k#v$1_CdzG<m&yiH?^0bkF4r!JTZk zw41#>c{Uptb}43HNqmw?f$K^PNqSMK5YH}x&`c7;6_HJPBH9pe8N{=tVpW`P^g``r zVbEkWsfT78-APH%$7??!xoyaq7Fm&vktT0jcU7-GrA)Km!8yJgj5`^SY2 zFMNq-pRfAhbc?KzR&I<=BpxT-Pz9h0YpWJxWJ;1pned?ivs4EM2QNkU5e(u}TJ`Ji z*Kit{g^?+)m~=zU4>zl7i2|XCT7v>;RL5et07E!Bng|}+_(8Qe5Ztme*I)bwUFDC< zQ8tGSzsqJgUF(C7vqIuf8GCrT!kpaoz|R*(hZa0JaxRUHTPUVz>aW*VRDZ_ zSB>Kt#>10-gNeq&hDWLnFW7nS?7e)s8qeYq1O*g-qako~A;7a?N!94iN^31#h+hn? z@#i7;G6VT3ly+*HK0FLjS9Vz2xT{QP*<8N2?@CNuyeeWq2FkNtyA}a>5no(;hJw^V z(VgSbE~2SZYJIq*X2$VC@Xd9Yly;YoO_{b~`$wTUXo1A9`XAJA&WtMng$77=qTC;i z2N42lb0!{eW25iTe$6-=aC>mXtKQ!(;wi3Z{YUktZH8966|*|4QM8`#s6Kdq4qL^a zV??xhRIDnka#=`-5RNsl*LVb4=8Sb`9|ZV6ks;)>`l`S9zC zM4bZ+oK2h(@@0)O4ALYJoi0j}Skb)$cqg%Y3t`bT#qlq84Qh?^7w4k$_*v7#KKFxkMK$DqFy?VX#^ z_ikxx)~LrxdabOG;Ah%eM*H50o1{vmeWrp&=IW`otb9YW6WEp zG~a40O4@u70G=tqOyrCI;r1{6V7#))Mj%A+o?XxyYJ2fwIn)v6w{w(U@+tH zLAGW%*yBH>`RDLG+%fr>JM8mG-w;f!vUwU)X@Sm0k%@(f+?~OJUPAa=;eP{QP!+~U zVi4Q7K<=>yQ{UJ)@wI1A-ybrJ@uLlY=tesyM`#jwggatZ4*Nd9!0bk`Xu=7xb~TU` zGv6IH&ioPRiiv5tVB-ik!O7e8@3CByv4*>CWZrZGE|)SeQ1hRBnbWUk7z11Y1NMtc z4j5tlY(55`8;PLo!^Z^}G8Wxxd}HkG9GrP6P(Q{vz%pzl0T%kCIcTS*rY^9m%{mGM zrWRSu0~&!owV+(IgRwyc2jsRo7u4cRa!ZVs^bDs2unZfKa-_#2^cUd83)&IZVYsl!Y6L3aCx3 zrWa_9lod9svN09|WEj_UW0;7X;cDoUH=EBJCbFp*%rO7@wzN_Rqb31|h&|>0Z18`? zWA=B}?Dj{ed`8N4xAZ=k=WObp`EYQ`+yL9vnSqz>Jl*ehzWuuTuExIWoDvVi2BEPv zDtz$3ja@vbi~IEn$Nw&?;@h#9YzpV4-&wa{A=pv`_fZ||FV3He;^yqT#Oc$pU{VqS zM@O=D@jy8iK756-Eu`e-1R$M;{mMWvO2USunE~xwE{kLQZy@&_- z`1y?olD4xCER7pT{9E+ppSf2e4k6%+xvs=ZC;z0&61dKH`k3dbb|E}4trGUw%$s8PEl zqk9AYy4lIt{&6!G1eCyW25B$$bc$Zo|fjeuy4&P=~bN6^DQg0nl**VEZB};!Au~&ap zi7)uneU??TjT|vcKS5E>2u~CdW>&ir{;cPyjZ3E%sB~6_8qdoQiO$f1KHcr<{@Zh! z@eRB1eKZg6!X>L6o6)uEE6HU^8mCV*&vLw}t?i&XT12kYWAY@a)!DxI3@0|C8&V|_JoakTDp zeK9dabcP@vs=g)*HF>Mw??OSrnj*fp`F1XUSk&8VPEIIza}Jm87>ijkXQZwhx-E0! zz++@VQa&E(bl3Vziz3%QmlYF(IQlR+Q57bmrOhdIq`2fexhG3=>tq!FsfDv5QMw&{ z^~)KTm{I~-YW?rM{q=mB9}$DLER@hUJXfS}WDz(+SLRUOm9a)gP5kc0O`Aq6*|GO& zjVU-27dUF<;IsH-Zn(c6ETaI=GXanR)!U?-mC+J}wG`4LF}gJlvYzOz=awFlk&Nn5 zgawB5{`BsbtnK{|ml(7nl>2sfr=flrr?^L#q{D(ApZ%Kj^9uXD7-tg*EBw|vs1W9M zjwFkTgEOrFcYsW(Kh`#c4Foz32!o!<|%}6%6c%=Fbe7;af%ou1H9GC~LHG{cP{(DW46ZtY9f^}Ce<>%$CtI(-f z;h_!^eG*ic$C?l~TJe#?7kyy?rogH|sa_raB&PUevX{4SyIFZqKvxkRbz+s6z`=v5 zKyL48PRr117!f%u0V>d3(O)GM5j zzV1TDxux|@pxmtBZWLM8gDTJ~`NS2>u1zA)JSqiX6xl$eCXYY=Uiy3pMECh%o)XNQ_AmC#D~B&3>?Fj&QBk6WUdXYmemwH}iP zyK;Rdtkx}#vqVS|QWEN>3q^6MD4klNyITXbp~mR_zQdU(fq}1rL0&DsOYB(%v?XLu z-D?IqZb~f7xMgtL!Tu|>bmI;^KfR#-yq5N|UgRD(DE8)47P0X2Jz<&1p$(1uSi_6iUUM@exv>Cb%pG>z+C7Txw;6iiH|&I{ z5xVHDXTs&%-+%jFrT_g=)7q@z$|2C1?@xVkfzjIx(i91sjaFl3Z~cbw!1}=DN@3PZ zaNe&6^{|W4+ga{lEbcMGMmO!3!f~Y1GP@mJ=X61qJ+G1 z7u=h+O-)3^oiKLCp+}uYoX39OSUT^;R5`K1k!Qxix`q|qi^Lj~XG`%%i^eSD*&;7y ziaN{Oc5u9fKPtKG!IT$J6XxIA<_v9aHs(QHW6+ltEC3_DurMyF^A$V}ygHaUUo10Q z!_|&CAOcKrr!19PP#JnW-l#kW)XIemZPNz&qsJ}Uy{YJIVgd^_{~t3&B^Eo{MZHsG zcR;BMZ%aky@5~xJ2y!z%X#`8<3|J-`!{|DymlOOHAPYEFVz6M)bzafIYSii>pwBHw z{MOUaScBqMZl?pg`iYBYqqx{H73QvTEB^@qbm6<6@4y^!u%X3+Ko#YYN`IVXedyeS zOEn(3;Q&t3bez8ltk@|Uz|^7{C{DazH;8P$CPDGspLv5!BA^ZT9Y?Be7?z%W09Ua( z{fr*H_;vszS%}sX*pmY!T4Q5lLq!RB4cH}^QhxAyJc6OIKWKA?P$#D{lnY-?S~?%I zB!7GkH5k zvTW|)nNVipvuD30sZP<89!zZ!xJ-V#^k3M$hfT5OR^k%DoO!*JHr)=JpG+}vCp|Kx z!t!cBFy7K5G$>{K@A!=Ic&8WD934pHwjv5&*j)feFG1i?2i$IUZijRmL=(9<>64+s ztKQEsVRCSc&veRxjA0D8uy_a|*`Odr%R)UA^xmHsQ7j%{%8LuzwU2*VqmM|6^*UB~ z(!MDf0J?|`aw%{ynh8piC>LUgrtpGN7$P#S{qd&N(QVd2Dxs;AJZP-7yj9)brou98 z_&ZWhO^`jYh#*uLi~%N)(L&CN2BA0xIw(Xxn|Nd2bxrpnTX}`oqqn!jJ0b&Z0b2xq zi6lH^b`X4{7$G9TOX8eRT&OjanU9yE#5KdOfc4U;zQ1-BIu2@OPCeHLsOf~ia}_>c z2;RVEaM#r997mX4fc3TFrvvu`{ejcU1b&Q65iOAuQQv)l`-rSi^qIXp9F-xhyA~E2 z?9kO)w{<1>kFe%Crh@63XOYR-OK=;M@Cf^xCP$!@i7CVl&W)m(YfepWe`PGha zXlv*5a6t8wgEgAtzys>UC&Ic~48u8Y6g!!0dW0k_P7PX!$1=I+`=#S?jEEvk2RCPV zfSoN1V^*TigQ}<&Liy&*UcqDq%B8p$5aYVxe?~sJ{WS{2GLdK}$Ip{=cZ!tYYlxSe z88LB7c&c8vR2A;vafn?+?Eu&4NBQYteEiE=1dMr`e=MAOW|BB1Zj&I=6J5E9L7V_s z<+1tzqCG#pgrvtTqrby7sK3b~tU4_edNdv*Q7*fC&AFSFJ?3;*7y$HQris(#|jE0d(ao3>7S< z0A1Nzi?`U^9kJwi%U<*?kEyeq)u94^Bzu(ONm&+sH(mN3_^x*N_FI3O*J1I&pBTH^ zVCpIb!Kw}@mMfU^(NAxp&JwhR*cwX0|f02$0|viaavUohgb)nL|rEE_F6) z7b%MVihj?zS#1kkuGWP}NM<@TGV>s+m9z*ryd~A+pt*M{jq8*n=3otco?x6{;Y+8DFWP2GfV0;KuqkR~MJsd(%DL7|ffV0=lb1J-CK7 zdzh*j@&J3-EMQ)jueY$KWuX@qI2g0jQMINRH>iCASIh?QbQ>h zL4EyxO@Jy9)1XZ;K5@txbzEvYj!;j8qgK`ZwS0{@$9{?*TZAR9hzX@JkvI(L~>zU^tCU7>4Tdq^` z;~ktZ2W;?NX9q2lVU(ZpE!5p=mR)*KN7bSqntixSPhIHs38G?0^Nc2WK&Lo48Bf~o zQ<=}Cn(`{xRYPL5L~DyhBbY038UG)@%S>lh1R8ih-u$($&tB|#cEwvzey*?^Ps4pl z&2a@*A`4F&pQ}`YXCC2di`vS9SO@ZkJz4quI0Xj1OA%% zxA1_oskp?j53-xB_gNjXVC8s{kQYqJ7FxCdW)Q=u z!rbD%gnWQ=8OX0zSY~ozS)bgr+9J()0)%E#h#{2;u#UGKzvL5oiY zSwrz>&Ug!N%K_9}wXAJ30})S_L+6&P{rUKPh4UO*UH?3~V@Hq9V%Ohn?e!X^Iah!@ zLMB<5mVvBbl`DA!Gt8^wRro%EwKh2#-A1;$Kk28@VuHL>%i-PmQ!4nn^V<5H*7h8` zhtG}(Ih(cU!k}{i`-m2@KsKp?ZOs_8%4!-ajws+n8-d8oZGYfJ3O4=*L{SSrmDD?9 zhZWV?c<&h0!Fx|&2xxKWynRXq;zwr;hN%Nlxqo~kgGJ=kKo<&NO=_S76~iZ-)utrP zNquTuL(rp|2Lcs#r$RXd;Cglku;gr<5#yH%8X&cPJW|4m=Y_XQIEA79`j37^G03YL;~(62(_q zNL$~ZbCU_@zKR7dfd?h-CWRm;w>DdA1VFPDkn$$^Y5pQ*gIC7DPE8^A7OLlmSKlKYJnkyJ7B{m=X&}X{_NOoBuFX5< z2p461WhqfRa8t3~j&lZM!DpxtTJwIOamaIZXiMs6BAd<)kx=Q5;@=5|CI_Wgz;!O1 zwZa+neeH&niaS)fd0!fWk`+P>7Lly>dpTAHPh~%<&U+6k2RySSa0wCTb!=Zc_GZs> z5Mw*5v1^3X@)UJ@2=E1BPuc<_{3^T``}(mmaN~qaTl{w_LnXM72hPW`qvMZ1{>Va3 zym;LLR|uYmr?d;DFx3-(1H>;C5Rn!$7vg-JYDbjvvE6kzmAKTU>*?tCB_;;zNK|2P zL9`KkhL*wF_3FF~wa|)gEK>l~!wy>8=H(F>MymnQ^Y?zJ3@JTy;n&J~Kp1MF-{sMXazhch-1s(J@>@1QYIdFOtlsf}+SqKt0` z=Qm@77NX&!A!@gXOrq?lWIYKIp@|&>1WB_ekRUfE?skN?ac+n7SbRGS1yI6KPsudN44dja7?W`Piva8GH6#2>LJLgvdT*ywN!B|1=Aa0fvHFm z7nfR@W(MNotAei5P*Fa|^D}bUt1G8e@-oAcEznTx1pwr-&ji92LxgrKXAQz!l zzah2wh17S#Hr2=K z*?GFg6kc9358_nsB87}=xFrB_Ou?jNmmEPUk%Hu`sd6(k79rj==mr{47XdhL^ZNAO zVFH{UlMJH#ybD3unD(95l4(k9g6LtxL2X90=2d|efUEN#jocd3HI5>>lRR0w4G4P6DzhG5sZE}-~EZx6!DB2hDIU(S1_#c( zhdxF+8egrNxR7_yWy}_ECM5IsaOCV|sQNR)`kL%J%4KK@{x0?NNk6WL|K&IiJ-OpD zbY{sV=8nKlXO>r6TLZ#Kbzr zz1I(%9MwJ+??%U57ZKv2(VZ$H@_O13sNaiA&{_e<>b@G!`l9M^sq|ON)=`MXaRt*x zvg5$re2zJ+qkI}dpYa%g?>54N{b2#9wxusGnrYj<6qWHmTXalJjP^6*POt@{$&*H5 z6I9!o+!?2-(#8g5D_r(}JZ(RdGKnTlVSq(ziWH9(P@6`y?rtQS*$;aM+CiBR zj^Qe#)(h9u;Q==M%U`23O5daXmySlNP0?-3Cm@#n+4s`6pE;IduyD+=W5=>f;QBfa zFtBX9gSS>feT50+1rLu@$ZCuk+_sEhaT8!RzF|{4%#6S2dadP%2anMKNmMpkc<^2n zVnikUL-gf>KWCQ>Xp*I+SSu<(iHTm~Vpc`#WPJ8ItRo*zYDRda8r^>2ZNJaAnEB=T zNH+!jm~DCueh^{r$^>^EWYpj%jomzNLW5Fw_Wn?~P9|V9Q2RN_syGo?)+N_oIoJVR zTrMl4efGRlACFvm<%#vR$13n9uHEX5(oFl!jgWkR`MixLTC1wp5FzEbi;0#&bI48z zyaL|&AtrylekUMTiG#vPMrko-d+#Gp2Z}G|v_B|n0O1sW0~7O0Un@M@p7+xQGPG~~ zHfFEws2taKK1lafH(Rz$TRq!YMc4)*@^0CeHZ5o`j^mUe$MF)}Blkj~BiTx_&;{LF;YR~ zOBfK%Oh<$~RS&o5L56$KINrR%Yq`*C zje^t1yyW!h%Vn6d(2vMJB8w+ul7=G}X5c!y8zPcXC3bIaA2=Nt)Lg$!T^%)swrbmU z=fIC8+6_MzSvzE=PNevEvNsUg?ROiuU>f>q?S!zm@;kT<|LfHlDrG&PWwnF1Altcj&!MyMAQr;ySHopoq~k_w3&sL&YBG-sJ*`FobvMW!ww+J z_#6KI{we5XbPjG@2t-&eIT>;W@4#Zf%iaaOTN18#yDaZS9mUL&6N^UfU6bPJg+TQ*K1r^jd(64zhS!q{Q@s#>yzyYf2*D;Tm-jmTwa*_0 z@u)Eff5*2Y4{q6dRytd=)&S_e2k@=2+XpeY!|^KF((#ekaW=dQu0_m0x$|G^eXRCF z>k_#OK%Ks5BL-R4M85P{1q=qVs>NX7ZdchtCkrvhG8QM=40r=;Q*hrPc82sZRk zwWDUozBzBKV?$AMN{nslj6oz!(`}Q}SQjXf7aImmUgWkj+G{%u_XdS;N?G-G}%<1wx-d3;-@>?~C{`*P*X&vP=N>yk6k+ zbRCMbd8HnCnP1tAMyagv`VLS=?}Dd~3g@`1n`nxOp`KZPUn;M_00@WQ%b8yW!!vY! zq{WyqV{%aIyT;TbssK3O+K?(%+#*#4%2>Pvs*<30y=e<|PxtOdfH?=hvV_E_%(j>$ zd{e!v7Q&Ex38W(4!9UD$4=uzYPHNh`xn}a{t8(0SZ8KwNA9(_ukUD-88zfSIgnrFH zz6)V<^BPH?_x_vUOe#daNae0~RXvcg(#p_c;Z^aprc(Bwx z0Tab6vT2@|bl9bCj+23bHQ3i5dVZq|^XHzIH=~}aT$(K+KMm(6eOAjdpV(V9YnqNm zJx-ivF#j^%!MUbm=XBzXwspKk?24E$w4E~90a*t`7E^)w1yb4-Zx0@tkVY1e{rp27 zXGPNYf=Ur>RMz-WD?w${cnDeJZ-BNSpc47geY`^WgL-M5s^&)^`cy38zi3@_eO-3X z)XFu$74j2#V=c83_KKa*Q}R+RZ79{cFU9MjcXB9+`wSI;J3fJQlL7R7P{IhxbrnZ+ z!{$3iL`9>fRD=%QHAd#kq@Dv%(6u5sjoE$x_wYTSXWicZn+-Oe&4}si?8tAnx~@>v zsS1>UBDMI#@p6$XmuSMkQG8K5SWHczR3g$sX85q$GxQ4)ZG`bWJ}0}Fty;+z(@+w- zpmRme!x1>NjN(XDm49FupAfy4JAkU1J@b$l7OfXN8CFA!ZznB`@cwv{QK0AR0L ztU?`za~Hega^R}2$?Jmlrlwk!-^Km7vgey28UlBUfk`P!H+5IFIE=+3Kn;dBJ~JAi zj56+xjgxWcnuKTPB&Abi$^-Mr>6+LhPe@-nA2AQpyR5^a($cTpstO^X9WuEe7znC# zGkhoM0hJ1|{!Pu(Z6@UfN&q@I7^XHIKhqT2pBL$4hHdqkUfyQ9@~8Rtbl?5Ahi1Bg zfdN5E;k>BeO<1ABys&3K!pEo*@R5d5$8_zX06>-LAzt|yxBK8j6w!|4l*@V$_R*9~ zdG5yY7-1g(2UV94#rPi1c8Un>+zn_0KWAUrH%M_C9xs*%k*i!o&|<TE51>*N7nlU&ZTD(${8wId6TL0t&D$BzDc^i4e1p5;Sdu z3Tt-j?5Hyc?lYqyORbkkrvR#s_b3@2)G?tZ^YW`hiq1$QA3!&zo0Ci=e?UILyG>sd2KUZ>oA%tLO3F!|C?40Z2eWp%=E5?ztm57o> z(Q74qU$uQBM&@vM&2vibQT2}9egLloiQK!_f9Ehg(DSm^X~ctpyZc(k4Q`QadBlDP zBL1N3^@xybyT+G2dhrWzGNpwoMIS3t+c)o4p*$2XYr()(O*Z zPPcAqmYg0{uVPzbV1cZLga~>pAp8gIdFA#=(?9t>tU=W@_bjt~D>(E}%kxjx+Nj`% ztbIK5_%*(llX)f4+sA5Nm~-uSNwTi2*psV4BJ!A@Zblf%8;SylQz=A~lFUHh8W)aM zBU1%khnt?kgRw75D5%wL(QO-7AN_l46uHv=tcD!O~55 zB@dMWaf~Vn!ce$)%j)*u(Pf`%hki0Nv!DQ-31v7Yg?NVk32kgE=11&C;Hj}g@iqkk zQNPKKE=%vz<=pXAlJ^wLadHIe^!h?i^!kb$CWdW++Jh9hiI&y#J(1Ue_#Q<75$_0n zryBeYhdayUjO3Pm_Uw02H!s?+t4bYfW)C>ANoWNvE5hG%=k0YPy}SKh_Kt50MMufK z??Zn(Gukc|w`l3mTV&>IcyJv?AEuZTLcFQD=o*xK6ex@p(rYdHv3U<|b*<=^lgapG ziYO^@umfVovDC`NwIEh}c+@KPx?3S6eR zMW(A~tJKq8+rAx9FJ%p5eTIA*>Xk%XFmpmn1;ABA!uM=M}SO!q861QE{&g;$U;zQ^$SAn^6jq!xx z2h;;+bKLV|p&w_s0!RkQ3XyTkiiktvr;n^s=LEop%b{BcnOzOAkxNcTvwigYzOLa) z%H^G(Q+3bV_g%qfB3IrIeG=AGQ)OOpb#mgx-CY7XP0;+eyvp>v@!h2obv?kQLY89e z>-7{z;Cpxn_QGT;3xLFh>sO~`hIwGGAEnXp?F9ev0M*s0TcY^a)D9ZQ@$^LN_9$+b z>c@_uNkT#lWM5AK_h8?6-JcmsDX%&TXil>)5bt*ta+Y|9Aw(du8L_-Bz{3+ysYqB& z-rUUs@oix;t{LY--p|q9$a*rf2PIc6Put_gVdtAtx9I(JFCAl8@*5|3FI~}+)7$Kd z3~}{+B~`)D=v};?i<;y6+<&wuTy;e$xTqbL3V9+&OfnexPQ1)iaTwZ6fgq5@c1+diC;JG zXn91Y7&2jxP`31O8eM62>pGY5M+fL|p zrJKfMR@8Y{|MRaMoq#){ql1~F8Eg-QNyuH<2aiFCZ3dG^OJ&c&tB!*Mf7N7EvBQ*6 zBkvfM9JBFIO$w=ZL5U@+)NYpj;^)A8X-54lA-VH4&J|E?n*WMS~fnL1-|7Yb@I904m0}ceafo zLlCt&a7sa5J5`vV`xJT~<^^-x7V4W$wKGps!w3}K{F3ozIwYYumP;PMx4nA6Z}ii> zaU-qzo}ZCvItFaJ6H1+JXD1Blpd3U5d>s!C$p|Tl zdcx2|{uQLpGXrrPkkVCX))7{bn8c|dBAs-y5Ilrl8H{fEdE)-HTWio#`X8-zCKkFd zXfp)}1qjTzvOsJhT1@{DvBp*E z?dfY;&>Ag$b>*a22ZTbhs6!?Ya7Znjy%FOw#2rX<3EXEyM8tzc^eA;-J9e$i3_sd6OgiPO3=v&kt}U22%6vdFY=F%)Vq5Cs`lNe=HlDgRN-y$4${8G#E#K~ef%n5 zie9R>U98a&&KPkISg!%+iGY7>30H8@!S}=3F%xxVplIt2{4_yJvRiDE*G&*ks zgx{vP7xWtd`Z|<(vr3gHT&jd3O4JUry{k|a$>$%24_xFB+4-~2=``mF@zM;T6VVSg zQ0px(G{w0VwZk$QaR?gS`FiQPBHrTnfHPr^Sg95Qg#iUM#5#i#%Do>_>?uHwf#}wS zaUSYcB2WGVf79`tO07hZyv`0VT5|*d!^oGuC8$hXVxX**?oV6$en2-ewsSWox@t*A zgDjA@YyqnxL7svVSPT()CrBb&NI-{fM(2*XIJg|I&{=o{*M!Q#Q$p~-avgbi{1`WM zb7YA`1X{++*XLozPQ7!2o-ux6Eku_dXy<1%-A36~cjDubP8fkrH~jX%pir>ku;-HI zLUrOyg~y-XF?W!mgbLq|+B}L&4QW6ILi}KWFMt@(ogSRipoS0hfZSt$s;Eu+-4qI( z@Vi+Xn18;{KGWoU)*GmKmVKu#x`ck9he6 z_#U^7`zHC{=|rcgc*f;0^6H;swfjKIV2Oel`IY66WHNf6>gRBaTHo`pc_(m?$9gsY z2w4{A5;PKR{QOZ|OW^{8t?8CEKt?B1Biw|}$O?fc8{%M%!yA#_ zDTb!DVCf5QonXMURu2~BW}#Y~rlyt=-8krpf<6aV-EUEoHR?yw(z&!d;uj!gpJl;O zZYhX~mm+jio4Vk5*IOdksU1{IO;D{_^|$Q z@Qsh0et@IH`=*jPCR!}saQovCz67n%dj(2}Nyu|Zno)RWl?jxVmZB^R-zUfoF+MA* zcae{E!dFu^x&AB1o4~@ZUv>>AR~QijnJ^nYpveKmJB~Lo;UGV8xO_G8ZwQDq!Nw5% z4svET)peNPapv4IqL_lJQ_3jSn~%g1@|3-is2FU z*a!lgB=}p8&G8w_wIw$_pvb2<%G*VAT@;RlV4DN-Mzg;frgRXe**e@wTvii;_SX}< z&K%mPaZJ&<8cQ?!I0#z{y@4z35CEl^LHO)R_5kV?!9>T$M}nGT5~;TiSv~;E%h^Wx zEzuFrnTE|Me!v@s2~;}hYJ>Vdet8fV)Kg$MIeE*VbJ8_t8r zDQ^(wBCKR}r!rAfTTNl_%r^lrCL@6`ZNPqV2SOB@C`p#IdpsQ>=)v;`CJFtmbw6bL;OZ` zQ2g6L5>2iQT!{!<5|E%H6#N{#U-hBD&8Buv*KoLqh{TEydeIdUNf@TjH+ug0Ji$xD zL2doC=?c>u4qP)9ov1fED7AsEQ>~7JosJYsjegn&rjF)a94lH^k1Yo?3FW~X+=)!J zx-wtTt^)t@gzH+Xbw{wGq>+KgvW5s)dTLe-A^>DTG$2XYVQRq}97)D8bo=fHAIiE@ zyCEyG7Y{(<`i9o1HWk#zk7*5A%YJ~k4E!W{)+t?sIQQCjHcwGm1mo72+a3UhRmUuX zpP!~a>_@7^QGi6Rx9GpAvr$R98PVln;V*l79v*NQMR<{BsDe{RASW15kLdk&*{7PR zf-{c(SWhoKavdy>EsI2=TnPiOFv8)QCEU8I@&u-#j zrq~q3iJ&ZGE&nLR$Y9iAqu>p!LCqQbe~q5kV=2pg%13BEcsi)PhNtQ;MmaXCpNSSb}|@5w0?pR|ps{6vBq35acWp7=FwQYUUwD$CfHh<{q6w@MP# zuCLVb`5VDEUA(z_^XZ@?lw4tqhp}100agR%z6L|Qyv`btvtxi36=-J21hzQ~v?Z7Z zVbByMh|iI1?MBc#Y=_>|C)hJX691v$9mcE5%gcr2z`qIlB@R5nTv3uf3z5|v=^b7% zGqu6NQwpZ60(sDa5p+Jr(*ZjE2rj}nCP{U6^3vaGRZcUa`tuYX5rB7qpQzO3zbO@HhYYm}x> zID~?lz|o0F$l)~ca(Z-J+8gT(0UJlzQE+!e{N6Sz0Do&P>{v|!D9L`Ux(uPK4yR4< znE=GDz3lcPjZwRSj7i`)ME?4NMmQoped%iYUM`7;mLTPI_~jyTmz&i2W0LP1W4Ol~ zf;z*P$oZmf24{A`REB$hmP&A!69BY?LsKiB#w_ zgHyHYU2p)c7PL#F1YbwOensVj_Brg_NQ0(YNJ8<{3Qa%`8$8TtSPV~o+PY}Z?^ALr zkD9q<)OozBjMUMv=wuxavmTDNdN~ArDe|kww;zN>K2lT>7Z%;IK!LUZ+Dz@jSX?hE zsi~UJg3s^->TRJ@xEXzIfc|WN zWq9H=RH;R_@I(Y#%!TQ-8#Ln}?35nplf*HU9Wh_d6GxK#Je?|7IRjhC##y+OAj#89 z;D!QBjskG>g%6aLp{cI`wss7z%wclxq+)g zOWeqf3KT(QS43%5tm5eeKfs#m%)L$~1D-T2zWbVW`2 z0{ezWdZ}hnK=x8?5M6#QjfLq1fr{)EMC>k0J{7q#&78o2UWYhHt{u?*ahG_FT^f=# z9WFJ>z8G+nS94BL$Qz7Tq+R5bfv5PbB2kyn&M0C9$J?}0>w^btK{Ldha>u`rs|oF% zoE4^1xVFsNx+UJKOR5T91S^Wqs=<0( zNZiWpJMc31SBuZLyc)krEO1$KWLZf3lxfN21_Xm=RA9m_qq8SCcKSzx4jqd&)0tip z^p6B|%7Hml`0jWct6};c0&eEfUwQ>|eXl;t5!pYxk?AH(+s3B0IhbB!ZX$A>B6n(xUkn>q` zZA;!a9%ZW?T_Azcj#2A*O% zYc(moE%z1Um*pQqpdLdJ7#%vGwPH2MrwTTX0}zAD+i(oW(x4TDOg7Yljyk8Kn(EML*i$293h>?GB2e&aG(LihPbrw8Ay zOj(03RWHo-r__m`(bxZ!&96ei(&_lW){J{1+A;XU{M%{8BxmPykV7$?x3R)h&z26k zzCoJ2Fjvuz(6{mG3>P-kei8OKx^g zb>nzHJYS=Jjk1V$H!Ob-PtX^B{1ulEDc0*J^J33Q{%M^y#kpOvWT;t(@TWE%-nr$& zzx2<~+IM{j5hVQb|1pO@xoZjkUX}L!=enD|xg9O1QRda2?R9Tn)aCHgB2hp8l7{rOL^>wo^43-xo{ZEx2Y z@@REL1^@d;i$vrM6|?;L=l|dHCOxsRTuBD@P=9|Dk*J9Oe0a}p$2M8nPx^gVT)t7= z=P|o~8ynv~;3|$aL)Y~#fph-rpQwrdrq6#Jd39CQ4pYw@J6#%|b&qf{{njgRo8OJ1 zgfIX8{hRv#KXEty*SQrhsbi;3OEXHXem-iR~V_x(S^eP&-@S;A2(^SE!t zw;zP#*yiV*a96tL*4NW6XA+XHZ(X`$x&nOCN+Y&F4L^a^9`K=TniHVX;S4(35;s&d z{q?0}ak*(^XK#Of21VTQKu9>wLlZ1mbJBhiBsGBy)RB|0mC6mR$r|KvdAl9F0uJB* zM~KD$Zu&lXw$Y?7Jyc>7O0P`Xg0$fi`j?A$G`sd8y&9p{`9~`^X=&S?_l_ij0i109|R~bp`x*GfDIGMB}uZK zleqN*;?G}%oe#RR{(YhOiA8+CPR!yC>}*s$65yO%&@o&%RzMV;q#A~76;VNt5)%Wq zM~s+o998PX*H@t^)Hx=q>xHRh0Rn@z_$Zqs=!Wu_@8$R^YA{Y(*J(NS=#T&$soZp( zmKOBCcmyne^nMTwA|hfiK;wV@*FV30T@3ur41#_dda8na-1_qEHFds2cC3}CjjSLq zUV~2@4Bm|6Hr_$2Z0S93g_0zj7%MPtOH`gQhR^k%>TKHd4VFrM*H<&xZ_K8`q33Z> z4gmJEMVSAhm-P^{)3dt(*U~OhaQGlEsGx-b=HP=1X+Y18SUS4~9i6Y%V=TwsGnOD}i; zGarKACrV1PUz7|Il>|{TO-4qB-xR70av;P^X%8S+1%jgD@Huw(Z2e7NeHE!h0ha-{ zt(_JJdpQ<{tAPgztptw$Zk#cy?dhLK_X+@TWFwA4o3#M2|52QAVPgRdA-uaL1ufvG z&mSsi`BYv`j{Z;=W*J7HI<}&_4((b{t;#0P11(M~Tl@?9$Fl_<(AoYM<3YYG8_nQE zivRKspH-!>jSxTPR5k|>pn9cu~Si)t^g}ZeW0Gv zDC_F(e>pD-AtV|Bf!K&wM*s~wVM3vF3E)#~8_R04L_ENRBRMSy43iTLArY?z%~}fX zM$c^=_?P3ZpZ!cOn`~YoJw|NwCb4f$KKcCHZ=ZaUeSG$k2TzKgJjq{pL;YcXlIFvM zg$u3h@+;4u87=nhVyj`JmwcoC?XXWj`|RRrznaPBYC(qz_SGLR{3_k3^o3gefPb%p zqt(mL!V8`@tbViMrJk-XIgtiKhr=}b<%a-_;I3*1h{uVOu@n~&((HTZo8g9@d5Nv@ ziGAhb?5y0UWC?70QsO}i9@*uz;X zodV9|fedTx0?u)>hDkfz{pc%Um+z8+gwjlt!xq6*PsTCqP#=f<(e$C zq!VQV4r%8Uh78acI0aXFZ$NL{U{4%5v4z?W?Er=*j%79w=w856t!+HdU8b8p)PkP< zmg<1ibRwL1nCC^?=F#BJSp{qlzV8kD;2DcdS;_kg>C*aqKf)%!phsz_>yQ;*Woc=d??>$+=g)7%`Qbkn z(L@*+`@Q^be%yU(qd#=y-+7O{E@(={SgNjpn_d1MZ;I-%l7u8lo3g0`^^OBFRETYg zDl7$dcNEIwS};@vdxT4JY00M_tSk~4{|@F-;f5_hgPH(niiZ6X85I@f!kO9|1yyj8 zCF}5scyVk>%2EhN&b}GP54Ol`PQr&vb-%Z32TXU>a4^{07=F8oLx_A`I03;sE@q5dcF%LucS? z3M;fnnwkR~S+1d>5uB8?2!D2m6A=uF2!IpcB5E0qAdE;UHle`jd_y|!| znp6fIOQW6!Pj1&#vA+8a*}2i z>-KgHZ}e+_@{TcEvn7@1zE~fZE9IW4YA>zt0QcNK&o)?^oa7yRJe;qv(ct_Vebtd%{#JUFmVMoI4_ ze?mf;h%Jem&fYd)bwH@B5RP)-9Zu_KV2b z9jWC);9=Ad{<)q8NyPU2cEqNc-ZKoC3y3qbGzPzZ;AB4BC?O55<-9X(+Trdx8de{M zm(K61A$$t9N|r*Uv?^cYIT&M0?W=QCU^*>vz37v&*jzx1pl^6EPujib z5|Yj(nV})Q2uQDYBRJ6mKPu_!yeHmE`i#8>23(Y4?BIcYUszKBlvolHAj>M{%cM-V zeYuh|@u!Mr#6?BP@jFsnDr^92tftlwIR65^De#pVjn(bcq;yqxrjLbH|4~G!JCAxf zs%XX11O(&Rqj^O#mwFX=h$$E10x>#zTdK4{Dd|LX6A|}ZS!ore$J9CrY$M)_n?Gm8 zx(be@kHRX_f_no`FXOLRO66Y$3`HJqn~;pec@ekyt8w$E;1BpD+fS^8dc*_NIvJnp zS7~zc@^v)D;bti6HFD0Nz_>Kx9}f%f*t-#w0==+gyk{no=4Qc1A-0)?uD-tD4$riJ zvpUPHe#UFmscTcKiN&JA71uZ)jV-nGC7?&m{oRq}+(U*bbB1oWfX(NTKshKyE1vbe z)s%*@__3_Hx%gqfAMW`W5||X2Y&J0Pi?=3UMc)Jp1IH_e+_|%y?}VGjXyIp8zv)PE zaWOr0vgrlN9xkWg71gKHGLbZQ3+KasQT6;(l zG?mSIi~{o0;g8lWY{YGHWoQ8hcmCcnqU@NE4K4TmJ_h!5NZ`H)cfs_Crem9e|DcKl zM?z=su&uW!x1iwmqLTHlY4xkrb+>HEK^7iML0?Wm3T%c)&H2@`xsa1891)@tYJB;i z8fYXrjuV^{k49FdDWtGeu%a{kIlnvgbHTT6EvvKe@%0syVM{81T$xkYlf5FYyTGe7CVlUzXE%4E4Va5 zN$oMMr08jf&|6}uiXI%>wI`v??CIR0hpwJq52-g7bHnuDioEe^HxL=&b6|5p2xh%s zAN4MyKbI7MCK}t?fc~E)E9*=J4zVB#P7!*y6{g}g)zS8jEw}sYVIn8O*AC;Rga7(x z#m{)9Gf}w3(cDtIpdZ3(X4)>==%V=+4cmK1Ski&qx=qs`O2S&o-#7@0M`+lnFc*nP zu*<=0tQ2C2V!=ZQs?rgvazRjUy?Td3*7NLNYX%*o9x#bF(rkK-V(Js_8l%t^&(qQP^mZ$_W_9RzXNPKd(HC`Trx z%R02t?@lvUH?k=k^eb(9_pX-yc#O3y62QtuT6uCW1C@PUXlO3Jvx`PiITtdG6r9N- zue6yDyZz`?!Ksjd{5V`pNouKR@5IL9U@;iMJWuumfMmvez5MM8`i>tiu%Q7xp4)(5={z`6QA4fTsY1(v z5SPm~m|FNOV@UvQCtxVk>bk9-rla0E8)+$+Q8F!^?}cV^MJ%I&vDi}f#W6}N>u7>EX3p_RQX(SB z7D~l8^fW&f*>sI|DjbHOTItoH5aAzQ=J1D;$t5ymwoZz@aOAYHm7y2nwF!ayb=i~2O-=1( zA)CvtYJKMn#Cg2Axj9>FZw2s<8r{D%C;jo(;@z%aLPP(szORfY5Q28>OXr36OU>{0 zHy_Dx#VpOujfqrev2!D!x1>As3@&;OiG3!C{8ZXg#zNX#22FLNeGW#A1yKwqW)+1V zpLHy^)24L}zL!!={i|pkQLXGlB`sDx7}nY5dPwe_?QR(x4*MIxrICl55gc8qaAg;~ ztYwFuo`kR!7DcoNuAg@z_dL?oI<~EP(2@)*+~#c4ESRmpxzZ!Mu|1s^9?NjMQwM8) z)*|&xLvngwoAn8{mF9EbT&L?t0$3mD}o>2;Yr+vlw@ib?4Va& zVo-l{b+2PZ90XTgd0GzIjg@>797Ux9!hKl_sBaJYpbHXkTypk@?E{Jp08Pk5TbFXk zhFZuc5pIXi<=G8TDnmUpqMJ@ZJJ327=4H4`jW-7W$UD#NL98jgtuZa;j3B(Yp+zpz zAwX3NI^Wt0+xI)uINzwn4wDMBz3#6pG_$Y3S)GVV<9J#}`0KJ1hYd+b=d}lolFxV| z?NV_>W0u%DOF6aek@94goAMvp{8z~0e>w4{9DY{~hAzoag>=E>yoKhjEG`7IZhWVo zM=gGU1bRN94<+Fi3j4Bfjvrq^ZR>d9G{MhS1k|(~KLlU89;@Cw8ymSDrEq>bw7oLk zGsd%{p>NrZf&hq%>2aQBgt~Qb_7v79b?97O*{b-bA;_AcHgB)USU_5vqR=$g;Azva zghA@OjMhd2ag;aXu_$(p*|d`F*2lu3+q_Zo?ix<+67DVe%*S|@I=c@6(LvuieaX$2 zp3O$kcq@+iwwP!D{l=~xZr)zgv*kgMTE-*R&uoJ@$iwr3P~Z?#v)x#|W^kZ$@0^SY zf9i4Ks)Y~yi9a@Dvnzj5Ny#WzlQ*O}uB51r$*hI-KK0{ZC5WlX*3&lo=NuM|qC<;D zT|>i39C}NuZsWk!0qBc}P;*liTEg)hZ=ubiyPVcKUksJVaaryJAsE9b$_d5*F^?>6 z!10PjH?^6`C=j{MPeoUB?TGwx1*K*zhd3{lrf8k@q6v}*)Z`(!2 zQv^u4xOM?IiLejajq7}}@HOrb*OU~A?7CHcdR^if>Crg1M_RajEUfKjIkIi~|J_lzOw=G_RPKf<( zsQ+tRBTk-JDjk5)8lC?`{r%|ljAbly>Wg<$*32Si^J$|X4%~7JvtlewRWO?H=#}-J z$Nqt@0ho9oFg-~jDWpPK+h;9<$Czg+$8vAvfCl`YldF5VpWp!b30Vv3ujyyln6_lZ zkzzglRS0|Tjkw9&G_fSAuVEBRb&@u+7F<-o(VdV&jW9~J@jKR_ObzJ-WUZU8HcwYi zPmlwzCd9gE$MEYP1doN$5F}B%&Y_>8gc?jFayy401|)b&H6N8koTlUUHxeF}O7(5z zvAFY~t_PG!Ez+$*FwYInX@?+v0sVoJf-cLpWDKINfgV)nqjg>^U9)joeGk5* zKpYSF>YE2#gvBMl2VVk&-1ziZP(6SXB5I-Lakv55OVV52y%jhsci$j#k*|du$~)d| z#n>ZFWea!}l13l1$CUbh4%?i&+}l0*`5Y7(C{Bv)YAS~R!J4q^?&N_@E%ufv&@Bt{ zS#Je43M5N@u#iIOv;bam-U^INy1KfdZypRDVsnMAIUu<|y*=hYn4wm!5guW}njux= zN2F_5`V)7Y-kU)yS@`{B!FEuRj}I=7S%BdlI~3p!Z>i(wzq$5AD(x$fFy|F#*S~Yg>Q97RQ zhcOkC+j0;0j^y-31Wb~w(l}m(nc?vMO0Y8vpz7LVI2bxZVHE1mTIf#q<8MTh#tkTn zu@*hT!_ge_aK5vFu@-AWe|YVWSFu0^Ods@I1i*2mQ!8BNqt~g$;8K)TKhmK)JNG#j z_JQys;t0qd!yu%PoPv!v(La|gdyIjNPrPeZ4N<-#rbw6-CM74G8N!^UetJV2MWLtuMu0Z`NNN6qg+%MUL+1PEP z$Bw;vxRCCIAY|Pe{nAU3oSTMr*;mjGWh4ELVml1mGqt+#B|bs?Ji38kP)!_e8}9sT(bM?7N`o@x#&;q)}BL|g@IB=%VT?w*_f4op@O zpUd}S&UsT$PeuBdCbe7!Jx}qOl|77Cc>W%~_Tw-kNK*V7Vw|kz&7$WV#6s#Q+=0Gy zJAl)W0y`BPEZNeGwtyCOK%i;w9xV+E3nSv{tpFMGy|V5bZ%{;NS^QDSb>P!`M~<6+ zCFcg~F~kssgD8B@+7Agl3C~zDQnyDl?xoL8e7w-18iPIKnaTmJ#*L^ZsiGK*hmeh{ zRP`*aaSvi!T>PsrfB?)nZWe3Lu(h**DDd10uLppt>$PJY-|RJZ#gw00fFnrKtA=rN zj8}56^mzHL8hT@HK$_s@KmO63KuaLS2>N<;JxE&HTT~jaVX~Rh#FCT&WQ}6WAevob zNM?^=Aw-NEF{tj|?T?(c20*!cBg;E5PV>y_v99FNBM>|jO7$_423A+I(GS$>c)S0H zy*H1`G5_C&FEe9&$1*1S&LsObl$~luWDAv~q>KoS{5tAL!U!OFD1Lk_2lV@p98MX+P#A2#GS2{I zf#;|h!_}ZCQ9L-~?9`uEV-I{Gb^ChW^4siuIC;wfG2)u2L_`E`!};12f>c7Atu=$H zF7J^`z5wH}e6RXqg*%&r_LQUQ=xnezE?65~<2Sn=1WF9#_v@WA(T~lUvC%LWkt0Pr z?zrz6_}!PN)M6;qJ$s>?a=y>V85UPDO_JNG=XK_br(h$|!hNa+Zkjx)q`y2&&RdA3 zv8QcR#(Ir#R*X73Xs1V8(x{04~hbPEx3#NK6mdALU|}??>5T`H#h@& zrxs=Ut*^09TOQ&WP)$7_J0}#ii5&%14`813weCL2S!uf8A2SPqNs$<*O~N$F+Ny;^ zC5M}5p+oAqn@xk-Lkb>Snze)P4$i+DHz1<5#pYikk<~g!XwH22`lFpoMAljpFwiL~ zP&=)Ntpae0B(umc4Qd+K8%s|sZ`@e$hcs={pl>$GTa`@G34GAP3=7lrGs8}vIFST| z|NTD})E*7`S26C$YN2tsqb zaSo1jZY(HazelECm^j_R{;gb{pZ4GxNK~crCCazJ8inlLhQ=)~61AlZlUK1Vj8d82 z0!3^Fl`!&Sg_iAwJLeunlQ^kvm0$FN2M7>!vF)+d(fCT8Q|xpFJq_$s{a8z6T=jcy zp7qv`l_pL_KsBWDMpGP#9+6Y0WUusd^IzXw_l4+0A6 z%+!t0j3|luB;@(hy?UIkLg#CS_pel>H|*WX@)#F3c%WHSCFVrYN+A?H`sC zOIE_P#uQaggA-V(vAY)W;1XE2E8G7(XSYWE#YB93yKGD5yZJ~v*LAKT_SJ`7(#!<) zwliJG*}w~7cv|uZQI|nMFmBSuh=!A}7m3KTHWcs2K8gflaO|ogGLRkgIBFB@|JZzI^H2oX!L1Sz(#qescx`XtvcaG(3Ela z6phTYfZRAzYgvBWHlun0#$>`PruEw4TAun4?%26x{j(>3yRD))58w#SW`EDXkPtK` z)%`tlVLw&Ej0<8SW)}81CY+H~FgXp!}8kuG4zR!K^BcdzE3J+DA_ieM#xF zQdXKq=1FMv{o_6^L%^JH7y2V;w;o~Ra2Rqu(z|u z_caI{;#9))V#fRX`cjU`b@w(nxzz2SADanM^C3BSGS*e(EK;2Tc3#&CXb!C!|Ao(q zO3Toxde<0KmWtmK@LffwqJHh4?aqSi%rGN}7;CuQk?D`X*@Yduk`zwNz-*d-cPPWy z$GUD=v@GTkoV?pvG)8jiTtNXbnEtIYpWbHG1|T3=d0O$jgFpaJFEdc(ZkH3 zepOJ|(yon3FGu*m3!Z^fM#Ju^imlF709G{hfCg8oNCCr>N|+p|c=o1dx}KkSGB6?#2JT(zUng-BUCurbl*FXf_x$j2 zB@A8i_^SfP7`^$Q-@AF~@bcn(+f}V+4|^>!R~iqyhQXxYF>#|BMi4JKJ}YENreIZN zSr}~63Q;U41OeBwXI${d1j;Dvv!-YRj7QyWXha|*C{tpRZ*6f3OiB?8qRgg>Ey}4@ zPa1s9GRADwI&@A*8fn{N!dB~Hwkhs?F&Z6{Vz~aLb5%EJ^tqRYd~`VXCpb)=nC6f@ zien+vvi)M5!6|miSw$O?1J?2dHQG9X&GIPw4fNd;CqdPJS-GcRH z^`02+oRC9edAdd2bFCu@mZ!rkNkMUZFnf`jX?{C6!T`W}LQ*XztE4LmSM1Hg^Z=_$ zcid}I=T8Hp)sH{*$y%*?O;`F))cX#@&}3eV$MemOd0yH=*ehRz+)1_IJxhkGI8g`P zKhcbDJKB5sgJcC@Jril-y*rI)`Uj~7W>8e2Y+>@@k#91}R=8G2*uE)2!NC>R^`ekV zg(gLUd~n=}Vs3e+^(86D204nuuKrIdt+v<>CK#-tG?UvUtCAXOT1~L^8Z9{$VE2dM zm(i7M8%ez_xMDxgMszq%N7ZcC6bNjD+5qMg95~J_i~RI6)tQedo$Ojc1yP>LS()17 z<(BOTR-MsL{_Mf|)c~~>p#r5e*OV1O#H}7t(j!qS1dYu&l~i`vR%xVQm?GVE{EZGc zs9R?U#H82|`ckW|{EHF0aO!Wm01!4Ld-c4k(*U25isFttxkiY4rPubVH*!L3FPI{1 z!*8ort39nn#aL?HENUHdN*os7-xIZljf8ZvPOPwdv4WsVjHl}+QiZ}Z^<`yc$?R{q zN^=MUZJNv9Hn(5kfI?V8Ta&9a0|1g(Pbs@Evk`gMgajWV^f;Aag;vm^t@|;jTTDgF z`evbMBy1H3obXOxyq#>c3IizY*ymrQeG8BL=f79$LuJy_roR$VPHB zcV{DznS-X$Q0oD(rz)MIOsPMxRS#iAIEn2)iY3tZKWFrxj4L08t1=9|EELb{=@WEW zebA$z3kTu}yQ_Vi5Qgl&C=IkstkRJP?DK(}dVvVEq=`Mi&nxr4LUgLm>Nivxnu={n z^lOY06{K8{EK8rzL+Bw$ErUmu(n-peUX(fnO$|zYsJvPs3^vZ3OKt)y5Hy9?R{TkM znRBUUpHa1ddTz_-N@Ie9iCBuq%t_}oD6Bk(XAE(zH>a>v|~6v&qrLHU5Aa}mFmak_)ORuos3 z&8;WRAgh<3vv_+%rc+7$3r^gz?Te`EFxy*wNI#HQ7lr_k@M{z{EN`-c;WZMeI~5jt z&8xut5*r-;LX^gZWrKqSr5{rp7a)po>@oG)sQA}@(-J3K@azK&^7c#{Z022eehVZW zLv^UJ^(0hfvjR-Z^)b>wkbTY$Gr)McssbT=lq54*g7!H|MV4MFyk%T627}6@Jr9&3 zQ&E81I~~npc6rMqZBd00guO^*guxQkRL=K0K~g3_yZONaFVuB7esIB41J;|*2X(Qr zOL}LR)lp@SX$)(xWK1D`{2WI64=>c8^4h_kRo|~TfdxeRtn%yj_M}PUV<{M7oJ9l- z)&8pKTW%d;&%(Kacmb&U1^t@1Dc1}%6hz^wrV@jJoPPiSt|9epqnUj}`iP~+*3b*Nx;luFF7?CFF)=h*%n^Dg?3?aEp3m=#TYF!p# zdiF3HKyvW&ar5HBSN{sE@lhjLIK1-2zj*wT27 z#JPYvG>^PCwC!visiZE%zze07lYSnkElm6iRjpJYe+BRhTXU?kgv?xo8bJ|A#{$gF zJT8#uoL-try67o|CPl+aT%!UY_5K*>QevUuj(U6_#5sXA8c3lI6MBv~WhmD{v&WZm z0>uU?{cJHQt&%zgg*3)sgU~V&<}BDRz7MRkW<92qg=q!2nGwRY0v=`OR8N>~Qht+X zWoD{CQNW5k2M}4mq%%z5%lUaa3bUbVod*AiM?qi@~Ny!LUik3=aTSttgBr@wy z4Z}9$n20ccg;Brhh-)}z?28S_)*eQ3IVD3ALg7+|9gIn7^Empmq$ETzDO?WoMPK3ApCeaJDGNte5b;oXc?|!AbL_JE#P@`W_l#BDj^v- zhwI^yIP}B?K?1cI(@uNm#bUC8(m;Qam z%Z)FD_;+n^LtSWf=$H9@UXL#$xUxNYNv1z`RBQcZnK9%>;V$OL|2`~mdF^Aa9gMy7 zv0$WFb%nO=pT}Kg>k?x+g2plC6NC2J;Gvri&pGd&e+IT1xt6G^qn{}&vzku>c}c(* zGy?lS;DIGZLv=~61oz(aLa+~8jU3?MoE|L4ytoS(%9~QC*lQ*{*c|jXiV@?b-DzCL z2Z6i91VLo>jy9&m80U<;U&amNjEbntegJLk4Bux{%o);q#+ld6&2^FQ9*mNaemXkn zr$B#5xI<_&BRqI;<%%pX2Ro7YgWQ5e%>p_w#3CM1Yc%hJ1Be{2tU z{%+zNM&g$3csYXX5$&yo?arL@ArG32f3p=jbk2aTjAxMQSI0R$z%Q0m zKERYCnRABOH2k?^`3YH$2yWOJJ%+%N_FA2GH@BYLPrR`({r3Z21+OMM8u`QE>bGKC z;r!QsmJLOu3rd#NjgX#63nwv8B9l`(tD-VAsM|O|aOHUx%`JL50_YG)(S@yPEA_E& zI3Ytsq@m*NQ*b{_pB6x(lpPW$ugh?&$uMHUEg)OiUf1y%5p7w@oP$FE*dhx#Id%=@ z!6IdqX^jk;oybApai8p zX%c@-kPk}w7xuO@AEOK~VPb<|jj@%BeEe2{p2k6-u7Tg1sUHrDoQx>2wrYL|Vw@ez zbFTA1&*{dJFPr~u9C{;X+j_92wB3c+m zP!6{Phi35yL5)l$HB(_^l=!rfai}3LCw>5#gYU-I657O(F+o^T7@T45#xe?kJ^GpP zL}J2v&tZ7Uy@o=E--K#Ch%$`j5IhoHM?G(gvD}fAvZQyQkL7tFyJr5EG~@gXIL!v! z^*(48Dw7{lpbi4{Vl+z1Mz0?ODPllqF{5IR0tp+}M4uPNb~byz*U(@phF>#)bX3^m zW~x6jlOkK~FpMowT;qo;hSNxC$Ev!(S0_a5h?H(}7#+dAbKX_ta0Cj?d2(bC)zlcC zU+MSSF9S1eLNv!dB*PjGk|ZNU8ggBS?hV~CGHWFg_HyN?{c;UA>MHK_o}F|yM``t3 z>5>C~Mp&MF-L7697<&m^G>EhPJfC$Q!;G{UABTnraJHv%hwW{Uo-4DsE$rbg0tV2e zX!EgUrp>K@MgleXSjvQd;YJ47PY4JQ{&D7#Q)NP8#u0E#bt_$ZUB(6*h+N2mSp=ah zN0jAelU7PN5@wxHR5=$A@U*ZUUEQzQ3a0DE4O@ais&j|K!(HjhU_t0YY)|f;f@T@} z`pkzh)6m{*K%z%RBs_uQ6*^5SuUWD{UzVIaGc+f!Q%9Sf)9GZDLZC2-nJty=Hshfo zDJbI)V%%WOh+JWJlJeP;Pk$I)NMn}X+EUEZby*uoNoT!5OF7n<+H zX|%QW#M7fk@*8C>Eze5Dhc7r`&PR$&XvF`MFPF36SOZ1$6v@zx{^?f?sW{Vw2GEe- zf9bv2vddtp?~9ve-Y<<(v;lCU8Z+$ol5GI{&~HL%%R9*?yu+!HYZLQmyVoR3;f; zHkc<7b#6-v8;qQlW)UmbCNx1^9{P$c{}6l z%rIp1mhZ>qu35jft=JKKBn^{c*tXB;++7mvp53$ooeBZMkEMb>ILY)YZfZM&_ioEo z^ix)gpZ6Q|_4oVrMsYT7qT$Q|$0umv5)cGMeZdR34Oe-TU6nKl*f#}l zf9m%XI%AiG-@Q!q?btFBe)=D~p8wD?{oi-k^0Pvln|H%QN^W)+QTd_{sr8M7C#BhK z%8n?^e0GfQl9Ain7`<$xP1`3TtLY7@*!zFIZX{%ue0mDL;0r1Kc)LycE7QJsL~o31 z3H<9#_{SeQ=o1osRI(DL?7JQOoyST1%(Y@~BRDW$CZ6rESN>2%EUXeXN!a=gR*|ib z&PVygibOh-JMMc~Ke(Ba{Xmkl<9t?z$Gx`*RW)M0Je2|SvhwKgOxUds#1IG;08DsC z)(pYh?MlCgr5A=^{r}-RV8FB8k+rZR|H+$kk7vpV;~Z*=wfVdA2T$HPWatz0kc|x> zt1g!FClxM|j1ur`WX}g2Wc6IC0U+!B!tWE2$EO{QTl-mLFOjLZlq7f))H5W10IFCd zQc4?FqRwtv=w%>-zL3Hvx#l0d+Rp-8&8bDNe882^F_U6kCQM2pTM^xg$H~w=AQ;IG zY?(NF_SIEC7o*p=qSO4(c6}UVGwL)B5dTid&c#Kua9KpJjcGsDR}ii#AZ90I0kDkG z7;gF@Qz{haimzZ|yff@O+?79igkJZZfm|70X5HGRSXF6^1PsT0BCHG!SQ(WM4A%KH zn-_Vo76v*UpI==?@}}=?4=?WzQ=~YoFR=``*Jd?}&8{a;>V4B+={A-bqfg-7{pJfK z><=y(ZP@w*)2yC>ZM%$~gl_a%IqFgn@@F8BPXWR&-`BO%pp23-{3OGB2Pb~hkny+@ zwp%c3e%q{{=*$-#ry1A$2&erNB^q9MzA``J;fB0~Xft|S{K#(X@($0He0L5vA&y2w zk%mKJ>uvk<_^V*tSVP4wB9Cv{8{c>h9t^+K-13E ze2t}WYw0qEx!0y@Ua$ZDj{BWydk;lb&L87yFGUdsBr(GfYzN-BZ9cgo<&UGQFqHHJdoY)TL4ixdyNR}c{@x$*{4y6I=o3)?NH5+6emJ*F z!#SifY7&D#l#-ZxL7E`f`2?&n3ScAxK%A$GC|y}oa|pKs3R5sD%Oh|k zyUJCVyyRM7Rsyj2B@Cnb_1iLkfGB-sN9(-(C)@${dQr?OEeuz~j2*UxFZ!Z5{N+`yHZ<5{Cu6!?l{Wx_NRmIb^^P+`FZ^Mj(6eBPmhf6`24^5 zsZ_`P_vVM9LmfBro1e=2{u_t=Alkav8vXr~UiaeyED;dwxb*y7MgwA`3!b?<_T;>2 zm%llF{(IAQeq+7w3uGfz2=p?}2U+Q^bxSz8+kaH}H*d_h@PQk3qh{xS=xE-ae8Xni z^v+JtSi}LRrR&&ezzb@frqKewO0sX7uPgf4g^nH}TjU>EA{rJHxITW<7zF_@4 zGp!Het|N>8X9Sma?XUd`+oyJqF~_i)Bt$t#d_M)CQo!g&^;M0@9it9+kf9h=xChYobdmK!2RQ4MIVz0 z?52;4@Np5|7?uCxo$vwmm7RZROvvPzlU7IemwJs+UY9ZUtb&Yr3BB20c3rf^{jb1D zq_Uw=`_a7GJlD}`s-sVg?e`vTu)XcyD*607{`Lb$`ewoK=f4#)=^uSRRWD;3t-i7E z!fwwI{23~~iiKB04F2{t;{##^=bYLV1hhw~zWXh5(~qSRF2=uAsQT#dKAL)Nn*U-e z*D+V+34GkQ|3?ZDRnxi-OEuLYdd3s6ZcL$V@&pLWuGjK>>rEh=y@t5<9|Mlrog|(}W{%-%% zw}S?q`u@wpk%ju|3H?hgBp%n>{Ni4rIjMc0v3J~U3nxw-b$UsS`MbO{p z_|}gr@Noq`uE56?_%B@nzeM1hgqPju6^m3&Q{8*z*DI{h-FU#juHUSxheMMGHownG z&McA;x=Z-LABzHWqoH*a6|#ed|jZUg?(8JXuD8@^!v)IkeE9_ooKGGc6uuB_dfHq18V z{tlP-XF2b)F7MjUuU`E0STXt8r~3yq_Z+o)h?&~Wm|@ZdD^?tP^jC>Zw5Mkqc+kaO z$NIcmzYl&jYQkTK{@|w{-}-R{KCZyuuRvhNX3Wg~;(PY&*PM5K2pxmWtgOY>*4Aik z61GXW4WK)E3J?`@u-+DX8M7+vw`FE#Ds0@i@p=B$X%rnscbBuA!W=EAUpS|<*$0D} z9bWR?#@cngAt6YgQEQH4S5!kTEiIj;2U?gRL{hD`D(Y(j?xjRh*uKoDD<&OBOG}v? znKg-mK(D>>j7@=EmrprISN)4^!!Lpq1XMV>TTSTjh4_!xA=tvo#|f?Z35i!RmWSDJ z*{3=W9z7byp+U}ESNCpz*_$jx!U!s6v!?vvSGfo;z}Uxuo2)|Su{a_ro-v#G6(FfJfARn?QG5pId03~u1 z^comgsjzzI!OxXx;L$pV**$^^7P;sQzlE+l!58)e zGd1`aATfI41W$QenJx5~CRrYYiH z|614KxcE0@ZZJAFx7J-f{ zDr#2ls?GiqCy(s;TFkXdYkV#o_PkCO-)UFC`dgY-&{~7 z$dlqCUVm$C3I*V5vSRMCc}I-!*IDlQP6!7iB>}|f&$S02Sua%$2pjTQgDLK}s<`0* zKyur0>=fY4(c0jlrv43C$?jFnoHZ*6BZ))FBm-E>@VcteeuNxyCwQ6?22D+QayTY~ zh}iUoI}td9;|f?B@Mkz+WIGvT0Y#kqOiD^hse|b9Hyd}= zVP{s58fPL?+~pd6l(Q$L=E#Xf{Uq+knpcrNgiuy@Qw~@IY+kd-I`$L^QV3}%O}X*I zr^SFZ*+WI7>_|PiEx4>tK_^lAhy&dC-JB;2JAdzaKy{4mVu zVwz2gIh3a8v?S^%;CZdN6b|}Yl18Su1NAsrQ&aQRrSLT%usz+Vt)kM0C*J%Rga3;m z7CJ+ZoYwVm?+*Iz?>!5+HcA-CfmgZGsEu(PpT-ouS23m_EajN^tir2?xqYZQp>1B8 z4_2WWg*c4{-qy$Ub>Q;iFvVPI@=mSlr2Z}vJ>ADu@gR{-O)D|xAnPdIs|WFhVi54| zD_t2D>ktb6R}=SDb#OZ=Z@0!*6~tcM6#G7AeRHNy=LkmviSCwo9r&ECxu+{)376}B zX||5b+|pAi^%y8QP?=g=N$F!lF{?HF7p|hRs%l^3_{MFeDbPIWamf-GPWLJ@&R++P zscWuRZ%i5Av%-U8`Kwu3lUbP9b2{ofn0-kNP&Sn|Lls>Y4!3JAx!4i`(D#{p88gHO zo~Qmof@JvSjM8RI{r8BPpL2CJI8_f+0#nrCb*A{M8=suv5R7!8qVo{7^j1KBv`d?T z%IF@`z6*P296*@arOlYnr`Qs1C}q3>)X2Az10H<$!Nd;7!++kMAq;tGGsp>g#K`5; z=bI81)z_y%p|lzH$M%VJ8KI0R^+4`Cu^!j?5W|mit*GIu7q5;Tv8Nrt;1i8xIOw;h z{Z9{AN>cdUt0(9H5Qv&6W00Z{;pT~{!j7*Szv2omGZIy+kMuh-K^e27d=_&%H$-Mjz&)U+Rm_mwxIDCpbU_WQi%zo0; zhTS#e$JLd;5`GCr;O#Z7G{68t4y*Gf7O3r+Pg~%{mOb}(Vw9QBtjiw9zu z%23s36FMJXz6_;A`Q7KFk*B~ z9LQ&uy|@)ww)BflixsS%>yC8fo+ipdkW}c^t5-Kq14Bb6q7wn_`O+c>@nw-ud!rA5 zYQMBNyHZz%8YREh!(-Rk*;*l5Cb8Yi*7Oq&3#*od*+$0m^e*_0y-kl(nT4&9*HUZ~ z)vdvLlKIZmg_mE|j<+zkWPxD$Rl`HYyHj{Q_pg}f9<#K&(vrgIx><74*v-S%-#b`) ze|B<;@$8VpOXTJ&Z!u~$>@7O~HE$&d81GtvfN|+=;TCd9A5$Thv8!!sj_q)|N-9g! z`d+O%3^4G{gx0!-%nP#pK?Pyayz?xA`YFKSsaV^&sISHIHwm8EI>?W>sCSJ>!i z>y3PTn7vPBmoG!&ZK}S#m+96}zvk)qn99Jk!8h_QUyvQrYCi7i+7D0JYoo4i@wfLH zau01b*s)_m-4~+tz~OUiyNl*IY1wA@UXfAqnV#eQW8?8jmG`OQrUT*`0RRS&_OQBo zf|`MUalnvh5kNPj4zA1 zypS?elba9xP0R7w6$v$ z);5$O?MjcX%)LAg{@=|NbTS z3ld(OUGz%2FXk5Wgt@bfeU`57OW)FnHf{m_0c0mxBuCQ&W~bD@i|;sQpz-nP9y0$S>`65_Da zNz_ar>`{I= zts?u0=ivPrx2vb~N&X~Tv_?zCRV0;z93-V4JOJaOi{;xF)wG|eNZlNSkPJYdQYW2z z$Pvi1&3HJUp*1!Y`PMkR3MjGrWx6c#K1RT{{n+-d8u{0b^g2!JR|{`mV~xm-)^A*n zSK=NJNhlf_8Nr4_DxLg50NM?I*3QQo)`%EzX&5Xmi33vH$@kGU4xw}}cSxbtA7%jfc-MHcFmK1I*2Ez^rXJOS zN*{Q)HxUc6F+P1gqELG2@UnCH7LlFr97sP&7Z&0)4!qF8m_HV?*o#npAJNN;?ic|L=n?P$NN;oZ`NRz)C{y)cbng_L8S}fh&R3*+Tkql zpE;M1{;%l=@aP>YohU<%Z|mU`^x}OG2y`v54Tv%>^lr34q};#dpw|4|1@G(HZx6(H zesP#68_4lC#ZK7|@ zrUAKD(+VE86erXEszA+_He+VHyUn673Nv+0=)BqOM)M(BER8BfN<9G8F{3PfKpT}( z52XKDf!)wt6IivGBTLYVx0Ez^55-K&7dsLtMC zT(Y;!82l^iqjzsTT?OpxZ4343UO2XU71&QEH}pbe;a(NJQ!KA!##~CxGV;fmZiA1v zC};%qbW8kU2tHB_;QEV{3q+XLSQ8Qm=*NLd9efKlH%zwyNZ2*Mv!{_y9)jrJrv&+he>jAkjzSDFF>jRDwDav?$y44f=m z+A3Ma2pLW?cfm=w-1cc%2260bUn0wF0r%Lj(tE*NIJOyOkli?T)l2O9w0M05JTemc zR8My>@J!ae-f$#70Thha0N|gYN775_n_1dqv;jzWhTdbidP35DqoV3A)>EUa?djpR z@d)it&dq_5qBP2v6b|k98t%huR0l!ApAl_P9N} zJ@F*NDp?e!n-tw7R#axztkRa52(<<6fFy95k$VBwd|FxfU=Tl9fsSZFfY>DdnJZ|8 z-LSQU|IkGtl@CwkT-FkKhZod6)Fod|5_DRNo*ZB8LSZDVQix81tiVr)vg()mW$t?3 zgUOB+Z`0#8a-U?qqo5Huj&E)~xpCc*DjhgFf|d^`MyU*jN*W8L6tmbee;n#oux+g9 zZkgZNkP4-g?I%eza!StDrsd@?a!$za%1TbtiLy$}T08DW>fPD-j^vPKvjBJP5G>i% zO_od#NGe3=F`g~Wi_EY(y!II4sk}nMI7f2TP*w}98}rT^d!ks#^5Xgg8*8fXz57Kly|h%(t&UmabL#>$vZJh`*m|iH9f{##`P2GWzEIcIW>yZ(Z(a{qBT@ zr`64GU+pPZ=eyabi`1UXBVQ-VzP}%H4*YecN6eLyd)vQob4!$cw^%sF_6y4${yNsX zW8!Zw`cARee`sXjKQoB%@u(kH;NuE>T!H`96*?-JAa2 zW!#X!FoN*kQNJJYUQ~2NclD&Y!J^9Lrim}M-=1!|HV0MD_dZ!P%>AP$5`A>AA6MYx z3Vd9F|E4SOp)1Tjm_+|&xvjwX+*$Ryp7IwiTc}1%d+z{5F1wI%^Ce#v89nwzyNzk- z(g*d%hxQ&yeV?HqpPKyN)$qT+-0f*GHWdcbUq6p{M4D*3dd%w}M{`S~RPSex7v6`)!@JjwqXGY29sd7n-1J{LbpG*N zJFGza$+-E4BZA`F<=SMw>6tQqr@gPTtA3X$PHx%Xs6EPCV)CoL;b)0gc79=Rq&ix8 zs8NVgT!DXM1@s*-9L(1mS7#ri#*Fq2x8&AcRfyN$cx<64-?4vti|yjo zZ{8vN8D;veR(3V(`p!kq8^`!GW-SU&bQpI@s>3KxjN`OsACC_@_-_w#W7hu*BTU~- zxg$iVxh+%pcI^jW%76Z+Mlw`izxg7&FvE<9ZZ~Hqn6AAqEsC1|sd@0U{U5Y_(tmze zxv=_SVN;#Uua-l2?1C{PtKKPCaHIMpeXKe~)VZNCWV31wO98|M3+F^ziK_ z+Ow5%anMCi0vY!?Kff{HzW>U?`{av}5UK1MCT7_l%ECajet5F)pqEl8^|YVyR8V=~ZdD%IM$ zy1JeKtmnVB5VIy5-9!V@jR>>|i*G7lkHL3^E4{V1YGnUe*gDZ9graR20qz5Y?soI4 z3Z3>C@u8`7qu1m2i{$mG0!eX3ie9dd*|Xz`#Rv;;#S>eNv?Y{d)Z3bZsQE|{?!+oB z&NgH0GQWJB*fs6h@yUAmtFJ(NpJXpFn)Jnfr?Lii4UdZSX??C|wQJt8b zwF|?v+tUFUtMw*$#xVDi-^PZCl6gOc-T85SiBIl24l-u!Z8Q&uW+C8?(MC;wbBv{; z9s&C~{krnhH($S6VB9y_Vf)B<0$ivz&7n~aJySf&1OvDyWn-rZW1&C|Iuy>uz^2iw zo}KTQU08_!(=H;fXAs+@GOskHoTuAlHQ7+&lR{c^%RKvH-rOi&_nybh{5{Yot|Y9% z0s3Z9JvQ9~#e)q5XpJ9~eb}NBzGlzNg>dTW?!weCWI=Prnz?P&*rG82_YqLZA`gSG zLsZ9u4s~8)_`JK|)HADEyY_ZYL$ zx^>TU0d;JV++X+HeNR5nj>~iKe1yqCkc+!MV~W^;0`Z8bRp1~F&?Zz=Xl_sg)Z`I# zflQz`Jn)jyO?!#^kXE#}&j?$G0pm%V#h{S#GqA6+JTR#qdu^@3zbZg9F@Ca5e+lIc z8-{ipwo5+I@@;(b!FS6?0*n=u0Y!FQKsh9;eV(d$mq|#Nq<`Bjkw|8(`e@8ByUudM z+dlZ1@XsJGT)G>{3mnQcP9$>NR^!AN?aas6I1gS-POBmAp^HK1kBKq>E(*#34t_;W zNklNV0p zF}q#Vh~eoz)5Pxi(iPaF%b~xdy+tGYMGLfUr*2Z(in;H=eN)8=_}0#c!I`+X-_blM zz4e*)>>*~BCwYVVM7CON!R@YmcF`^}81 z)XjqT#RCs_bH%(UY}Z?tLSDMX+OxO zLMeb^_LJ{Q^7=yd7*p};;K75RnExuX9I~og$CXFhf$cq zk15coh{v#fzl&mcr%_YzS$*f>^P>th40;;;_R|-H8$LPhzshR}ff_rKvuPrqoEBRk z6+Tnh;JWz=cUMB9@_;HpX;h=5@4B>*!qS3z>g2o2^-5w7_8vKcVpo^M#@$7WxzgEy zvW$QNu+8b0csBh+S&+IO45*D3h(P=wT%D|WWju7KuW|H!hhp^PyG5A)uX;UP?5sBa zfX6i}ET%k&inEfE%l{0{{?|J1vTD-yP{|V>xWcu}7$EhX*Q=9+rwcf**N?InhHii& z*Tq4zcWPY_w}4Oe`4nX15osu>vQc+f6*=+nrYV37P-T2hv6J-XKY#l<|4B5kQ%(e( z4;eC~=-7&>hhXMTexfCDzcdDTHIutjt{NUM6SFafy_toj=cYov?$mtqofIhdd#a|8 z&oeh67>44LKYrfJ0p4O^d~K+)Amj&c{`6%m_6z{Pz>xzVvhF4T7Q;yNL&gxiu)(+l|LyLMZywbP5Gf+k(iWz`yuz}nsMfYYGI)Nx+t<$DRZAAsDqLJ{& ztWdelT(dnSFtEkv{;Tw??83q(Sy-ovkjupHw%1gN$3xyiK+&Dx6EbWN+JDklz2y6# zSC_Np*EI%@-eCK}F@BP-QBQ-{Pw$%m9ckWVEXEZlto%-0s_7644k!Cy>eBuamEWnA zgu58FMlb(9^i^7vx6j^ejcS;^B>=NuZLAV|Dxa3)2JYPY%WyOMZX@=MVwgnI5c}-| zqxE~KyCuw1d3M?DH+78@uoUFEAgF5P=~zbk#`cXbQz>y%iv+qu?1=Q*Q)?w^W1%B0-w=2rKfbGYPWPgC%a0Ff!;{yHF$an0WG84AL(s~4Lmj>^H%5a{Pq_+zmB= z`&adXZu0`vUXb>QBYqDMdw@ksB9a9#Szta(DoQR@0@H~W@01Gvy zK5C zPDcAyhS}Rwo^C^j`CC|&t zfT8)PkTV0}!TRbJ$aE@QKd*}L_J z)Z>hptG4ZJi&MlFVkzu>!1CrQAb{U&qUs{YX-Qxaa~!PZkGjB{XC`uP8p6DHtd*9ot@Vtl0Oz$q9~*RzyGvR-x6` zo+K2egHvFa_aB!A0+IlkFSq&_#tpXnr_j}Lrkxq2r%k_+WWpgefgDq4p5%D&>p>QE`AJ57Oo8i z``@{H&DposD%?acyBS zODV;KCeG~Kx`b&_@U=B(U$1BAS^(JYh_$`Q4RxghTTvs9y zg7Et0gbSX1U@N?$KtMEsip=1-3*48mHx07dzTiai>LlHYy?!PbJWN4(RC`A_=4_9+ ze0doTM-5)k>ud&%MdfCVc1Nrh{zbcNolkhAMm_z-E^>cAZ+7%B}JQYopeMp?9jhKp0xxP<)uwl6vi8 zS%BSCMMQqtCfcxaW3z+H1 zgIdfK;Ms!<$>3GdMOx$cRd)8B(dupp(no`*vg(ZlH>eIkU2^e(_`_m&82zWVN>DjC z!MM%{gFG}fKd*x`RDdZ{{+*OBA)1jBWl<&F4x%o5`?nxtN_=B+g<1~b>t8N>o&FS@ zU%!g)urn2i8Ui+f^UZ~V^@L>mis61mYrPm1iM`HP!tILv^nVl&^P4L57=xTJXQb+K zD9>$1sW1YRF7zl*EW8bWKWy{*+?F)4>l}Xo_0`b0m%sI>eyDp`K&Y3T)NuhDLjQ&D zz)&BoO#t1>)yvrHg6utmQBX8o-Q!(m@k6le$*mgqL5I}oH4k;O^w5?+d)o?xtq@Wz zn7U7Gk5$m5&#rZPt4O2*>l*Vj!VqTP3t+DN(%1U^p{6Vm%v7PeVP9q2 z-b_j;IC3S!pnyNbn6~82=}#h2kC!5$h*0yndr74wNTnfYfsq7ue%!Rurp;mXY2A{5XvGSe&MWC_(t(42{@0Ui=8* zt9XPQEmI{F*fmZ>9@269C7jj-#FI`W!ToaXlOw+VVz+M3B)a$}mczzGnf?o|< zht2>=Ri{JkU2?4WM>g;ypc-3e`1pFX(#xRQdC1&B=Cv7|E1;#(d8yKQjqP4sP`v-C zXx{Ia(`55Ytxs(At00LT$_G+5Vym|1D1Xi&%*s&nN z>ik;6Te}>THNpEHXJ)#Jhs&Z3LOohSahP(oRqmy&|M*2-Bvk-Abw;yL2PB7VF-kyF zb`9?Qog&`iJe0hO!2arlbl=nX(Y5L1H$EU95Gm+EZUg za!@4p63bYhCNVIM%F(t=1X(9las4i1`%ottioeO=z3r|gRuD>GC_MitRyl6i89nCG z8mSzv=ISm8*(O8f7OX)2droZCumTf{K%k9NEb=mcJYm<@ zoflTV9*ebSC@EIZ*_&5~1S3P^#MTI;WF!V7g{eu?vkS?f5*muEWBktXVn=Wb7Q(Q( zIK_$HwxEV_B2x_af7N+{1@p3+qO&jC+xM1h`9`+oC5 z9i#B+;4SuP`L67OiM{0O|0XjP$^Xw6BvQj5i(S|jFw0Iy2?IouCgcj21>-sDHf<7& z5>O{5jqJ0_D2||#(L+njEnT$kbEB#Kd4a_#P|C@*rAha^RAs+mki10j5?4FBYrggy zBz+aM?3=Q-2Mo~Ny!rUp>Dk*e6qIBfW+|16n_1k`*b?vsq z>|j~FqK$OwH$!iEmi5%r4zJihH8r}b`H@fk*%RB&TMyh`cVe2(i1y}$_Mzm*yZ$<3 z1$3;njpURbEOD1cxrT&Gp_X9Oalx|}r!3wxliAz-qTMzFM5-nrr$f`=kpvj{4%_G5 z4Lc?k7*VIOJYrqQdI@xE0&3$CHq2o+FEm~(X4z!9*0o9`(wH=D+Tq}^n2E^n2eRCz zTM)DNsQ-@hJBBPfmboveX>y`^|GGlkcE-w+^wg}`H^xamjD*hX5?H~T;+;s_95i#-JoM~uJmiH9>d zAz4~wa+&22E*W%ETW!jPyzA%nHw?$LKm-h^+J+U!q%vgG#LF%+L%BTPqbA9MY{Sdn zdrw*shVRS>4yx$CT6Z2w_!Fx__SYZi+M@`>?FN||Vh@edQC!6z)?gMhL02jX%>^xu z-D;5!^=mSMi;9W{&D*n`Ss>|3xhTP2`RmXj+va6SF2ZvuV#nKl2ilTdt4PQMB7F5p zF%I_nTh}i;b`$)5Y$s69oTYP=D;AOnJP|GGp)*%6TefW2t{Yw4OoR91G=}UIBYu%r zUF(TE7`>+=^lB1{2&x8+YKhIdE_27S*~2tqTQu89r{s{OIgsVL zZxu6Gw=|XGKNUT>L+*3>z{pF%Qp+aZ^*<(cXk_bC#p;s@En^h?3F|2amCVn&zEaIjD@GDjS+HItQLhZ3YhgZP{VbNnzX{k~hux0CxTSvZjGo9@t zM-KTKu~pl$tjmBk7so1jtgW;@CgoP5&g>iX>xH*gU$lRk8K~;&>Y5o-4?fu~o?`BN z)CTN=16MZvdVplbkr~0=H^!Pda~8v5W&LVadC%|A)ZN%cY)#iZ<-K}_Y&j@mo$^n$ z%rNKed3xlRwOO^0hsdr)OI2^ESbjT7m*4jq-I2BNtB#$|s{m}bGD_?# zSS%Hb2#yTzx?cWU4{5zsk@Y(~mMGi=F(!${ZYiE00!na|O6$eSC*SL$zFVU+>5#kZ zo`E9Qq#k|~rIz(UG=5Lj*FzJmkrn$vx|p==-CO_q`;woi$M~gwetwZ*^~h}VN7X*O zekS-g+`t*D#Nx{%Q+QrM!3seW`fsvssZNdz1GD>#VzFAVC>KKU{m%RP=C$wJfNo{d zvhsE6UDkn(Px_yZs*eI^Xa$NZcjqiCfHBmr*neS_*ZtvT5 zTlzr8f3?pDIFNg)L93ZGk^s)$e#qjDpgc=c6k7tj6xgip8E3xzaiWg;lv8Gl`0R|1_ueLgVZuf$%(ek_S>w>+43zpl}m7T>U0%e-LkQK@C2&EN#R zH|GOplsAhgU&l490MlvsWRK;v%%N6tXysjde(COvdzX>3lAq5IF}kF$rnkL4g3r0D z(6C)e((I0}CqL3|R3a0jXvcIMNh^4~*YXE4Ht4emvK%Wfxt=(YL@sJDZiaX!Sol-i z#KF-~`nLE3wwjy53_3MxCzg*{)<+_1#YM1J`XrKA5CQBhje~SHWakGJqTAr0(nIzS zky{p@E^OJBI-}#M;eRq?c)X}Wo90AvkYlIi?a%5Hg7V^QG7m!N!W35-aerF^lG^IX zaO){xy&HnNWVZESkjBK_T*vz`uuD#h)$7t-`A|RR$PGz0?d@TFbAnH7LJ4v*wIyH{ z?3pQZU=EXN&(__W0MD(iZ83)8A0&%pN0Qv6U=e4 z4oylm7ujhUK*)p9I1BO(7-0+QudL>}M(z64DN~#w36Y4&DM?9RJQ4Si>d&i^eSY!x z%?P|Apjoi~%64V&GOE8&n-UvDJAFUFww>c)L+X2o-3%EMEZ(-WNNFkBar3C+h`}%e z!I7UC)c6Ly88`t~V^FAiYb_?&g3aQC9GZh{{L9IVEl}FyOAS)O#j@=5C;dz2i{#gl z?Nnfh1Y3IK3B(M6K~0$#!qPJL8YkvqtfMa~%7vS;u~ToGi>38~lfHL{#W)Ug48zpp z%sau7vQt8WLaznQI(>3WR&8TY{YdjiCPD*Swmv$o{bCyja7bhrd`p@n%r)sNnK4GnD(UwY<8k3!3q2x%L{l2n^ztR2Ya~<%Gn>FGcD7 zP=fjT9RcED*{1PhS9=emjt=zz$fpPe9_2WnLJSA^;VDL8=4q{U7`AAvx5p!yWixpMRXuNrPgnR>j zb!PT)sYBt`l?J)p#Fp#KG2DPraqVr*Frtx&2hL%(EFw*|`N;&aBA!S>VlmeCNDpvG zhSbR_Jy?27>R`{o$1W&{o#|S^YzxjeALaSK@u4?LX_y0Xrz6A6%QGkZjR!Yeg*fX7 zHlbFluhc_-?~Xr>zEW$k(KT<#^AmmPu?PSYP?e_WvO1pFk3D%O2MJw z8)IkLv^N)){oTUC{CqMC&&Uvi9Ue*0_@zif07>+xiMhc!*7 z5C_{7Ka2(RH$-4X{j}ScPPAjw&7MCTHr6N)@1-(rMp?NRF%@A;mUk{O$eW&l z5g!ni(ALEMqrU4Vbb_aoX#OD{5)$mYAZeU)ctT(~vLvTZdoTEg>H3nmdQu9LPnSji zC2>~+_v2J)HH1q5U%TlHUCh^1V-~CQ$Lpi2QeX|eK=l@j(hyTss2U$*K+JBCM!ph$IyRnBqsv%9je!f-1d za!S;_su|w-7Knl-2vNmcjPe?6ECho%g#@{66iUMJ+%di6wW4l)ad-HNlT+`b()Se8 zD|?X+rxeN7;n`l%^Dn0N_UG_i*!ar?O@~#?>B=`6#y{)03 z?WdX=YsEFg#hki6lb-df@Cln)80~ZHK0WwRU=9%-bAaLUWM%)ukLPE|I8)D080td& zG_=CtNfw-Jalknj2M7R-+|c6An2bpsn7p(M0B&mo%6Z&~r!dFB z6+Obv-(TxM$-j{L6bfP0l%*B=&(&D*K!!mbc1n0n3MPk!b2iT{wDY2-Z|PS?V>`_3 z;rHEbqdsLX53Jeq>M@~`214r|L$+p9+=lWy(C-M(ZpCjo>_m&_SR#}MDLKaHgzdkiDV+_{dToFLJO!N0(o6x2r|(4xAQdnt-r# z`p!q!mUvmfx+G&7K(sqK)Oh=QDPLZQ4~Q0fdm9pL_|bD+8JY+?32wh-?`3peee6@T(3WN z+{m+@Rqi$KImaAh%<-rq+QY474cE4|Mf7J@_qMpVxQ_W^;0^XlJOwNFOcww!O;u{x z__ohIf6{d#I5uIo?N{)1z)-cai=O4159ESadct(lzy(k~c<@x3OSB&JDt|H5vA z#C@u%DgrH1j!#$+TxBHO(vBiz+kQa{TA~A>#^v2SDsStK)ca`5ZjEbz;F_vh zV3Gdn*#m`XsTdO`efinB+KmiWBy=?|YXtq49>aQJ@^OLS9)hpk_O4|8pY|K)O z9JFyKDps0@U{D!x|)yL_Kc3W<5-3c0aaHF$^#_g~Lsnxx-u6OoW(lM|89|;@_Qr(M0Ys>Ht243>wQ?+g% zN?hNE`=%8%MX3yg0~o4Lzp+F{c^>$ndxRApR@efFHLKeptKER-N0mf%?`*frQrq^P$#J#p-%oRe-{is01k>1gWSwo0{KkE)_pfZh4%jG45ko>1B0!@fMm zN=V0qZC{2tW8Dg;2kwtvAwDmqB_@qBhJvo?4S@Y%$6+=cOK5~SLRpqOF%lxo!s>A! z=SE;(a|-!AmHQ3nz*Z}e>E)k}UHnU-JAmE3?wH9o>$5#_G8`e(N-f-Zu=mLb8t-&_ zPAgIgkA`OhuXwdkTwqJz*t91adk`Z ziJcX(FWYfHYthyAU%PU_7aY)KAEFA+f1%5dVZ&r_+g}=(i+MHZu#UO3vpJ;YqkHDq z)!Lu$Le4=lj#~t}gd!<5L47n%s%-vEJOh zuczz5fs^}@N?xe$9U`r=CFs=21(oLSl_6+-Mo!y!&z@FFUBWCLfUHeQ9iI6Z^j^2E zw6ymM7$ZO8Xmez3L}Jy34}K zW4}|t-aAeC3y*B%fFa43`uZN!96;^c8H{o#a+Ysn&hVqcsHYYK~$rYZ+n%f$&sDN)tEn0%ItRtcvJ6)K?n zWa)>|c%0*jSMng%!*uLF2LPlFHN)Vc)>kE~@>!G+_&;r3u_?N>E?)~1IqFb|RX%Mw zypOLWg#(!dHMkd9{q0_1CM7R&+)UDK^pDuTESioXX^Rrq;YaTqr&=UNaFmwK?=4I4 zpQjC$Q*mh*0d@C|Jkgpuyek9np_`V3mxZvlsQZDcW(k4QKZ-_kbfTE;omjZ5(BT)0 zq?4Z3y6eLV`ghw{6&~xkbu=LnlAr3{KYQ249m|$26Bs2Jb&_7z%b{=)i`_ZDv2|j9 z>%=+8Q<=X*I-6b{Oe6%uGUQ1w#O`wSn7e2f3Q3O=LKy^jxzH>kk+x}~Y=2VN_05)V9v;tr^)nKkohMKWjX~-oFxZ%ITuJ?S)zhM> z+QT@rs5VBe*ZO-)2NcH8fm|^t8u)#YUAowf64=-z1&#>p8#F@dINX=tsJnjs`W6p^ zL0dm9u*NGsbwNg6*|G1NL0I4{BgIbF>O){{&&UgqHfaflUMZhbJc81I00sm|)_!XIp`e7R0l(7-&*RLloReW)XO)7Hh)QCc z*Gfa$uWPXiWmPyvcTg#M!V+Dr5o&#{fu|5z{pY66c?Noi00+4hB0FFQR;kR_$0rKm zVL`;u9oB~Tl73fcuFySj-tP*06Ul(NER;kp7WW!{a8$EIj=#11O4I&$79 zz6$(TXbff?mR-hrf3sryh&}I>p*L9vuA^Pr1DvHVST%~XunLW===l}`yni?%@ zTJ>A7YR9utTAf|^iB(=WMr)z?icfhiynw2u}M~U5tr*WYr23c8qnOHE;p$1FTp)L&!ckFBy>8R!IY4T@XW0K_!<@(l*D| zhaXlRiu68lmnYhP!H=x0R&sY)u5k3okw=gE&GlHrEZ_KM4N@k&KT9J-d&y?3b;WV4 zbx9N;wm$X?Bh-zK*qL+DS{(&(*Cq~@as)g>s=GO>_>~)WqsTUB@c`;Ebpgl#x-A}W zznEei%Q0gwb(b1$^!Fb(ufcBdz{{)55mKL{WFM5Z;{#}DmMDT*;3h3ebrvV*q!*2l zlF~ddL^=uuq^{nP%g=v7@96o%iwiF2#6E@sB&!<(cYW{Pz55u;xo|Gm2E>FLTY{sF zyh@8(O!px-X_#>p&;DuEMl>xmfs9fK9kzAg>1^ueLN!8R!j^__qMY4^-TJzF^>?_p z6=Ah%;S$jeYEX32-2XL(QQrM6n9n&)!WI*sJ`H~m6t=GN?g}WhR~62+yHSOPk)DV$ zpngKgg6_-ROq2^RxRnSXE-TJ>|Izl6ur!C7#lo!&hDPv_;#CA1q`V+&rP=d`Qpq{lJ?`f5RjfS5ErRMKRLJ7^yq(3*dmfLoJm~ZdwSqewskEoV^7cNNu8jdK7@`tZlq}wP=OP@B#h}4d(ZVZODVsN}M@j%wkCsL?uj2|EtOZqlHqw?9`y&axo5+Y{hM`}07Iub$>Kt^|{l zOjTJTw%mUB$qAF9b226wOf%J7CbPvub^g~l{4fT;hqesw` zPaA9ouT*Wnx2Gcp=^Oe9w{JXy*~0zHYUuQZ-5yvDM1Bi6eUbwLcpy10S(b!8mYzTu z>^oQ83!=3q##xTrVKM8I35(|U)o<+kxv-c0b!6%f9y{^=bIo*M^5yuF$4&#Er0fiq zqVb#yR^40akfRFJ=AmpV>ej4IfY#WJF$gP0*JA7MY-eS4vzLnXGwP+^i^MCElx@X%R-txp(a%)U|M+uXzfFU zq4pFr?kOTleh0h?`w;TEz6m;A-H37a<61#FcAdxo*dxK)`f|C zKNe{%YSTb0)BCPTfI>;a-PYbaFxV@iTQ6ZXjPSE%nB&qcOA^_ph3_^C111i*-r=6> zYZldLO3oy~oyuHH2#0mce9X7cP;pvIdh2{JPoJu`p@EZ+WQ8&x%HcIIbuFvW60+ls z-4*xE5J=`gjP_53hu>cP6z`m?oxTV~4@u@$=ruU!O=+?j()d=u^<^$obxJNEd9H(m zKY;QLqQt8r7PB8glz3OQ%QRBtb_2G{utGGrm2-`6_`EX1>!G3BE={*63>$-(9N+N} zGLDg)g~*wN9F&K)HG7#_NGTYx1XL5y^=*_6zLp!=(Ev=dtx2SyuSV<4Bwnb+zeN+;Kbpdv4_oJE=T}% zYFlOYh2dV36Nj8Wsq%Kmm36 zZ=W{s-W0IuvZ);t-Gipw*yWcPG)0??mzp^tN5E!T$W_gH;GuJg4(A#sW}8R2zdp!j zN+G1j;v|+CwuyHpFkI}7Fcf}hGw$MmS$h6%(xWM{jkxDCqw{C=> zT33c_ZeFbCb0pIg+;e83QHq%+XS@nM0feJEY@9+Di#QkHS-dI3v19`{f*b%D2~2eA zxc1(M;}5kY4~%W<-?5%co1*_ zJ6;y-UBAdUU>4gPduCutBC*MYyb$r9g5^T0VjMIjxM)R5T6M467E#CEN{&EW;z^mf z^@!lcSM=@2y!=u04Uw_zz$b7wJG_7?FQY7)b9E>;ec|u17#Q(r$@Jr!K|SJ($SdD} zXc;7C62uB6EfL`3-=;F>z524;6wfW|-1kk|{b4b6ZE6Va&7pFLk8F(8dUjEGP z%V1rQ;=UZdR&=YjEPrpL`ajt#5~2@VP`Z0}Kr)|?y`pZ>G|?-6J>VZR zy!=Lt8kNs(3A0rB`*O?93SA;d=wJRcun{IPX(2X-x%b#t$1;`%p)Cn_l!j%=uzdo7 zIy~Q5^qe2y*&IsYxK?ac>v0(}wf^&}z0rG>Ull9*?Q?+ICk>r-@llY#4giNP)Lq1w z^77*y`kwtF=H^}C3wnDqezKX|mBR`;npsCf~qUM18r}OYeWVgy1D55pGfKuCqCG{7n2-}U$Tw3D~z z+G>x2ziDx4upSGsxCyC?G?98U`}KIy@F$2QvcEtVlH zF(;1n4d6l|jqvWljN1hneexTk}w8#MLS#V2~j%dEv}1fhds$#VhXS74sN zW&-1;p*{8x?&INTYoG_Y)M_%1A{pJKN9W z4P61EW2w(1^dS1<)c}l1^izpoyiTH@sz>AaqMsg`jyFW~XUnqx&cn(HG!WjZTBPm? zJCb2G;E4Ec5n>6C1yUc3JF?Lt%!5YZCu^X6|M|zuO?W^go{&C+T{y2Aioatn;ovdd z4~~JkU+ak$w`pxeIsfy|I{BOAwXOppov)%m-ilX6a>{n@ZG{u4CWtnTbpe5+zbm5u z#v;+4tiG7kvNBR01b;~lus;78 zO#pCkrmP)(YMN)wUD*~(awCdb+p6x8h`u9^B0in47Y{B2of}QSn-m8GO_>Ao#A9Tw z)!TP8S18en3jyX2UJsM|%zAM=^FdBJDGO8JIsDt(+eUM(q$D}qTBlVM^a>#3$)w6Q*bZ9ez{ewZGU%mc5^Ue^o4(gi#(rrY4Qx#o|my)p| z%mJ~fY3LP=(p44ZV(Om@q(Kldvi@v$0vQS)R_grQcONcAm@bnNukC#TUy%k4&?gzO z1v}yHy=|yN&ZHcRwnniuZ>aTuE(iQS?(VYRY~8vQG{`4+zo?tCqX{&y^bV2c$+#b8)UR&YixX`IP9$ED!Ged-^SDdMV%mj7%6`B)K3a6bejK_wWD^j z3_HCI8+hyvcYJ8fH1Qoh#>bY9&PZ81J3~0%c=_en8s95|OPZbugaAqT)T896xe%7PhWd zR$7zSD6va~P`Xs3-A%GIRTK$F$Z(mAMML|9D|gBL+8K+NV4(fqiX+B?D(s&wWWQvM zu$USe+h+|<%pAwsYX}Hsz0AbwGzLDrKU68Ga@4@*i}CJE>x}yqIMXQXspi>Hxkv*? zle4LzfqMWx7%+N@$=x(X=Y0JKJ_zKR@G6TlaMMo-kG5X#(N#YOEP&fiSzQgGj$QS~ z4dt_ZI@3OCf;*^LnoX%(+j`L%t>a2hAi`DA?K2lCy%~`DIdG*R;ePI|HZg%QtLE^aMQb^I zyi{@r&{MW$oWe!h$D|rQ&E^AYJ)5^)(c2eSzc$sPz=z^@hT7CmP-vPw%jxfHMV@6i z($RjFc2MV)p&o})MVX9*&OzL&NYwd`j^L|+cv*TgChpj#qu|A&fj*HC%OZ4L{FS@u z^hgVfLbd)L_IAS7?bxZA1Qu96g$h=wqSqnhoY0d7L3c6 z%SVu^0?><0zoQ)nXX1P0bV_99C*_DbXX=MfgABWq!k?ee92oCh z{FTzOBxS?&Vw>gRadF4Y^`A`yNmroNjmqiFU6Cz6@HVNh5tFX0UCJ zT4q2BWD}uAUJGDdUqZ!LEp4w&MfWEpNLZfd(J9SCx6uqJlmN*9^rD5Kx3>`}Bw*3aCZqtn zj)O$r;c@|G4)_sNhLi&a7P#Gb)ijU*xBVo_sWx?kjvP6GltYk_@<6W=gDMh{n*@w+ z;H{S^@D(c&B_!s8Vk`PpuN8QAL_eLh78RrD4@)l55C88D%j)aU^5m@7#&n`i*x`V# zRqOW?dnT1Ef<9_1xnE^jT;a_6R*0phg61re%x5fKH8lB;qFh13<;XA`Zkf~j+84}J zDAxvK6WO&MMOm<==<@!Q&mtM9B{nSVTQ{f4n4yOCXY^=WaY`#N0L}38#^FAK$00g$ z7U81GWRfkJaXa9QoevApVUm(4LciuFwn^)tkU< zKu!QO;UKWt1$BxMV-+k`zz-t2EDQOlY~4Fx0U=Ex-v;J{6OivFK?w-fB*hSjWl$B8 zMndV`9}o=a9!OE!j)|7QBzay0n%1*5(W2GRNRNv3_6DUzY3RYY_>hw}*%ToGihL<4 zzJ3LMlMXQzXcBfL@SD!sP5M_?E%SGVda|h1*v)?c9tF$Re+H~TDb;|m&7L1 zOU^ft(mlPo#&GArV|slpaLur&lXhg0)jBQK#B?2vaQzMs{9OscbgK_Nl{*fETVF_2 zwGBg}|4+&1;?7BLlTXtJqPRDBA8*4BGzH7rk@s03sio2}v57pl`J(5lo*`t8sDz>E zsqcQ9*UjC!Y{FXodGOptKKIf+BpP=V!tO_NM7k*T=iHC)LbRgWb;(Au44|4BEZMuf z^$G|=6Vmp6+9e|c(}2T)--s?hm>BD6C@M-X?gEUu<)_m-(J|k3!WCl6>tBjbR#3E3 zqOMf-R{R5UBV%sk=cQ_ifNA<*;Q*jNc84d}Ok<{r!-0^(lXoOoybCEPkzoj83JufP zMlb_CNm3H`B3&kd*b<|$lt>mjX*k~oCG@pyat$RTj%_w22OV8Mi|{8PS9_z^n_>J! z|5}Wu5yK0(lUu^YZ{u@#zhn64A8 z#Soze9Y<@1ar2VSy^8x9vRuU1JTP6PDRO$av(i2V(aSy@ZORIw>2+T34-wqDIE=vd zJ-iH9dxB*!>TG-QO+2eAU`*6iV*~)KoYs9UrJL$)18JHgopzw=89RLH3c+x>8g$&5 z;cXC8`nvhc1N{{A-%sA?3=YoAgWj?;XC}H;1H>eNS%2H7dva9a z%Xp{`5hG}@tqG4Er@n5{vnbXRa!?Y7Y4AhCXFh_Z|1!81ldGqHUq*)JpJc9f^u;wc zU5>Zu(uab#4xOTdpa;0;If&CxcR6A)38ZYYJx)2MhTiO0pH9o^`lWLd;#TEuFfD3m zoR^dQG!tity~h!ySsohHN3P5hRX|q8h#>_xN%I1z??*DQv7^3z=y4Oxdo!-ZB_m<3 zjrS=s410on4Sit+HiSRPo-^=L)k^>fSnZ@Zn*|RV%86xr;|uIJ*y-O-y%@L4APg-z zCmMwKg6P-K<0^W;74?OzV<_VDK|G&@6in1~74E9d9ktQV`%L-=@fq9WEAHusOG%<0 z(Ym(EJN zNE4&jloSl+5QGe$-#75qkBHm4PFls;n+!%lYd!=Fw50_MA?hXPf3MnQUVVJ!?OmUY zEuM+i&|m(95FL~hNG^DD$Fn>TOcT*{-HmdVR%^Nj1{s>WoY8(`!Nt9h{A4sQCP0sV z+j09}jwbxos~J2{FFAHkS?P_(p^n;8{X1j*g)fPVW0MMa%b^5}5rqmuXmz8yqJ+20 zM-=zYnj&Bqdp)nZtcBIuCM_z>{|nPpK3U;UL22XE>JbaEA1z#(`uf+)4tc9A-N1c6 z{`}n9rPhDF2O&fMKz$BP!>?<^t9IPp5Q&dw2?zzoQVk6}Exsinp#{>qGz$A^3NoIC1@BnoP}Ba_kLFA? z6@EfB1m=h`y9%AK)I0w)0Q-&xAecI`Ghi8X?W+%^M}R`QV40{KKI_hU(iK_RLF#nq zU!M=mN&bF`V%5{jG&Q15vljABll2JZ6pjnj%e^1Z@&-LX*}`t%#d=m`gh$L*pcopAdWkW}c z=O!5I(++CgkR{&&Q@>cx)r$U3Oezaa`?+C96y$YTQ=l zzJVN5c#x!^9Ig?)FkaD7^96w(Wb{k@$4W7d8`$WVHf+_Gf}g}Nx=w)MpdfTMH8jY* zf9b+61#^M6lQz4yEp})dmcS(msH&FP2ewkB*M{OS9mG_x1$H=oh{N-YJkZ-pv$`V_ zpz`!g=S{=xVcg4+=Gm1d&weJRu?;o26X`mmOr3Rs;#-f){$k*b z{pIy3L2_t~&#~8CYV4t({u2r?0}AETso7}@f1+j1-EF42oA-EoJQmm6%}=JyrgdMWEo-R0c7EiW{7UT`dZcnMg;E?o1}hT zn*#<;EmFq98Aa6N(J>!>P_^-K&@b0)`8%937VCs86g~OiWkILU^*kkY00`!^w)IF8 zeaWJSJ4?ofADiLiaS(Kp+ak`Ngka)ysIRXhCm*y;xY{gFmV1UYfYxP=^2Ur5^t679 z(Be000aX?liV9P&6}@%V2fOax2YZZCW2Vt{5 zgOZ^by$JAD^(lU24&RPiaf|3l$SN}NPDAg7{i{6)wQYx@chX!Py$6@$Gu8zK&L6lg zB;J-PSjDB6vJu%?S5a7(*tBD-_zOk|UjR<3@%_p)>%)#gm(uZn%GxiT69s?~g9*UL z#XshGqgJd$7r@puIbQ7e`bPnZOxecN6Oo?9Dk5g`|Hok~u=>F>8QTO|;`JpV$g_Wz zX+cUH^0yaAhIz%?G7u4Qlo>6umbJ&yy*AvluM0P~RV@J3Dsp5!s4+pUzW%7-z6rFTs0m zy0N@Lacp|+)mVeX^pe;2^UL#BlwR^%S$el(r%!-%M^)J%oA(^G<-LEKC2RH7pqt)b zfBnt;9JOcu9Ts2dZQpjmW%0zowlBM?A39Z|f6#XHL3l*ZqiN(Q0y?wr)3FWxkED~! ze3?HiXU`3RO(WXu@5{&xj#}F#nLtJp`|cDUU2Z0>OVTFMXw8slopIYQZ+B&8J9>_p z#^q+w+wxfUgR@>iJCV1}%T2EE*#y4x+SYxu-99H<4ts9lRM*#_R*hrk@C;=QnKOGW zm21`-7>qo=(W7gstMb5Os%yd7^+?+LL*`H7T)^nZdfDp_$;m{eq|EU^l9NX-p(Xo2 zIS7{ae9}2luv9NqYa#@JN6Sr{oxdUAM8*dA6uT zKO1~T_O*o9>z%n$hSykmVP225V|cC{91wqU^a6FYeJ~w=Zb>+lg1EaznD{t`={Gd+v#4WS0_?<)lH& z#cjc#?msIt_2<9CAr*711CsV+MhRFm@L{*%6mQn6)QF=DS!enzC&np zVZ;VUtZ}jhtc^K*P?&;t2abA3akO5w)TqV9#YM%%F_-pTFmd4dZc4?-owM&D6&Jc6 zX!57T4E!C5BYx-1M358ImQL@qu3sx2d?+EY1(R_^$Msr)J1+X^p?8j5o%_yd&C&0g zFnt{>LUdG#V3_oaemd!YPFCW-_0GmcmA@|$Vggs-_(m$R!&O}^GDWYVmkunNAAcJc z9}gUJWFu%O?v(Y({wgDSg34bYR4!RolJ;9i*z}*lR)M) zsPSTr(^m;cSagXUrkNNT)HP}th-+#913!ReD44;;w%S^j0tbzj0`(bk8E$eMk z|Fr@9W=t=(H5i&0cUJ~{E4oVOk-Ry28#YWJH-`1Gj4?(Kozm@^lhCj^p)9SvBR5<0 zT`C{*xsL_f{tz`bC*pZ~8<-29F*H$rb;3+cfH{HGulOVpP0kx+1@zUIa%%fa%Sw;= zcWpZ+dV|z0VZ-jcn|GgMhwwF{v|tNI-PZK|=_li7j%%xW*VkxrW3)@4!x7r`oF>e2 zJ|B!|4tEi3yYS@`G4Md=b75{MXGjWFz@UC)SIc*QU_w3hy#bbpV;I$LuRqb{X z`v)Son+#(6J}^q!Lg&Np)ui_sI6U^r|A7h$pR@bn|CfDD)JSd2JQA%5Z9q>}wI?Ct<8>@JzH|BqW??{nu>0`nvcwX znnrEWh&^hps;C0T(T<0Nr zQ1lZV@}8k5(%Puj?^2Q_l1Kbvp@9um@i~fw`H+*DBUjYf9OrGNu>0u5q&M>oow_=T z+_N#3oP%iV0O#80&l5$&$3J1)S@}D4hGEF;F|nm)M4Zh`x6(yhj&_IoXBl7CTKU{z zdqnB|`}u~D<`K(F?a~t6n8%akP>Mump%`0E1`Q`Heq>UAD^S0FaXivn^3BMTqcRlA zrD(`QfJoS15;*sOYTN5ZiGMK@RXpxA z<~_xgj@EBWW#f-FY#?)N|75J<y#eY_jO(GETuIe8n6B!Jk84P2k=?j34b)~NNz2zHNFJ=NfXJ( zI}#Ee>T1z8Q%OmQc)EM7n8aeYlErj7VBO9qt2`@YsC>YNiPeN9UI!T~37&Z;4@|gp zV;&0$i=uF8%s1Ns31iliuCDDSeRXPmlb<#<&Fd09Sg!y~7L;8_SE$;=^VL4yzjo?B z@0fZ1;anPS({}-K-}99LKoUFYgUJ^POvLl$Kem;bIOKjh?_u$_g`zv6!YOB4s-Tt{ zl+&mc!yTA61Et*TR8`V-7GWX?cc{IpT^@i&wq}S*o=78JG(kGt^=ZZ$(b(a^3!}i0 zczByB^cMao9g)DyR0hF=R&xwXZKk5LS%=+x*MXm9&FkSSr=Tf1 zY8|M-=#Oc(LheiSJXu$3jCk%V`%omNTJ6Kj<@`>mAAWv&si4)n-xds-lGXn7(@zt_1)BC3_VH)VShZ&YczH+m`G7Zj4QMyUssc#4 z;cf;&%F;G$4_h)t`}ZUchSGitje2||sj|DZD=RAQmWf$4YcK=n$Z5z~X5CesIUjmT zio<1Q+Qedx&Tkz8>!BPkollw=7#zp1r~sQH)T40U&oVOfFQpU6j|T3KDL$h|n6+Ux ziQpgkiZ1^gQl2BHnbQ^&ArZdx$?>%3Z{18L%Y^iWrk)g|Uq7q!j)9F~4=!7_9 zD-&F(2Z3)6(Dn^3Rc%P}ON&e@#VcW%u<#K~G!ZT|+(u{LjE#@?rDz6N{M?$-;^NP! zbU&4mp-eDy=0}_Hd$|bQC%Q!JPOlk4zU&ISVyZ+hU_e&rKh|C>*9(be#z)n4E#6~) zA0N9u!wc|}Axjj!lX%S|^5>gj2!4BuP`fuLd${*%^%d0W`%YdL?DnYUjz_g|n?=gB zzuqoq=JYo>%tP=>g05@cpqQ7Fjo>`Eid)>a?5+j@6G}2+YjWBN=QSK}J^i|rJpoXaumH|G8I36R0f{%Aw}@{%;oPvX<%8&CC3 z?f2-ax#3PPX)0ss%3M`FZs36;C$qN0g7;zaCNu?wntP;fy!X(n`dx1(eg4?Kw1%2Z zP#SNXEMt7_?8|hiD>h9nZ8@pzsSow+yozZK&tldyo~XmQSYK-4wiNReOJ|#=rW)V& zc86^*94;N(b<66nNw0dNP)+120?Oqm`W#O7hVcCwyr$qQK63qni}MZIkbX#uXRe7ei^)niEn zFI`hpX}(yWzzJz7?i$_N@*G5q_pA#xE-MS>hU6IKc7bUfg(!$&s32TJ|4;hGW3+h7 zhKoL{Y>WD6Tr6C+&BTCAsQNk-W}-jE7m+!Ne)>NcrNsXLsuB|a?=A!3Exw2}Wt8jJ zx`a}5#U)R162jAvm`j&bf~HJ=?H7goV(i$lKB}VjcBCm0L>N%L;~?q_2^V6*jgRW* zPEi{NzG>=~9D8VVV){p=W4;&vYoq?(03lM?CyJ4GRkZh6hh*H^|N7Ew(V>|FtDN>f z_)YqOvS0PaUH+n@s>e00y~-?0)Fx5aLYu^Sem}~!xl@ICJyhC(Z&NaL&ys;R#;afnL*W+i6l?rx3YY|ke4$9+gc=L7 zF&bazQMpgo6gnnPTPW(gzP|xI{}Wr}bm-7W(xQ;6 zJc@avE3_5n@QH1O+2nMg0s^udT{I1@jLDfVm(e|n`H0S&XH1GHg&;TzfcNb5AC9{A zK@@5Zix%d2I&VZsr&quVq~z8}ufRzd1#_YmNUXy#zz8E2Ch$_2cBHThY6>9yEEty3 zcx~@Tun`2@F>zzpO+zzSx;9@Doqyyh7E~1^H3u$w`OVr+x!UhLg~x+_sxKJ>!S-XI zuw-rpZB$P5M+VnE{5%0=(QHl$^7r=-e#hweku0H+#+kI9n%H|rvqw%l=mHo?G!iOs zTZ;lqm`cT3yweWPo1WE++H?j!P>FRcNy~U{Xt7(r0jEm^UH)2_FU_b(LVxhWqoC!4 zw^D`jQXqJrJbBWpQgonmcnyx6#u6s|K}tI)t@9ZbijpIxRNw<+hH}&1F&juQ4ZD2K zimNc8j;8+{Q{-G12m?9TO4CU13@A-BjzW=}Ysp$cQrw8xf~n`S+Pu40THXpmU|uuA zaTrUrEY**uaZb zpUN8S71&w8{%00;w>KI7pp;lyd<^S*`n&3W0bw8$%AiPIPHrxWUho-})lAUE*e`Cs zoI^wN$mzFbG4u}eSgjWy%Xlq{o=5({bN@p7?$a(f9tE`qW!PISH|Z9wz-8Zo!Vv_K z#o8CHZ(T1T#?v&uM|M?3PXgNKV`hmybp04Vl<~kK6l!Ccs3$p+lJxdpgnU3y?9=!0GB5#6n-5->j0~$P^zU!)5jAzyE%<~KEpGp` zx5mj4n2s3-Z*aC3aA^ZSYcgK)SSCkLGRy|zgFq>FQ(PV=5F3oeHue@}A(hPg*N+4l z3oWaGKCRg0j>$EFn2{Jg$>V6{@orcXbVVpsiSEfE&SllRNd`hwm{j}Mw?=;Zk(#?yyt zR1(k~UVe&(KN&f>5WWuM%$Aa4J)bU+l9Hkk$+nk&&Gg@Xhcu}^cRhfVYXBRZEK}jf zuuf=>%{0=^a{yqP!znzXGx%aRvTW-VM4)`~BbxkGln{u#||Vh9C&=#>{)v4Kx<+TKufH)`9KxxFlpBDSigv^D4|Il zCzCJ=C;EW_ZKbqV>dxHN#8ddN*xzuxHS@e%LSy>Ki#AP*bV@kDp%y3A^MRN$a@IwT zt%4aOc{pN}ozlLTmsj;9_kEu5@aV$+3x*~g*Keq)VFphvfLR~tUDLaBM6aN_7E_$D zbPj~uplS-r){#HNZGSo0Kyu%lgg~eCoynm26w*wOi25lc7?qx)(CQ_F7_V}a-8GJr zfOdVGlJn8S6VJYxVwjD7p?$`Ksg$c+fI0c8s3@7s$3?}?<4G(xEr0vvJ3;6{3n-9Z z2*^UuEg-e{s$o9mBa=uX~%BVDArHCmqd3fC8Aa0%M zy;(EOZ=L;ZDM_6UOpW2YZD_hfpDs%kfUT>qFU5Y;Gt|f_19iyp&%( zEtRJkDW?kgs)}Hs$kRupTt%;sgv5!zr3Z+9`M-+8B6R&izlXK#HGnjt8k;yaZ-wRt z(ONv)gB%oMH7 zayKri4dCkuN*fBvoNe~x4$G?xBw{871&>9?J zbEO5iEJdt%hV706yJXS+c5wNxeSTp4Fg5r;C_fcb z0)GC6tv?%E+d6R4{b+4EX|RtpsIfrTYe-&_)iuIAJ={rAwD~x|t6=6R!Ym z9ApIF5UmL=TbRV&RkWAPzIorETu#lZ)c8x=C0`{pd=A}(FxLv+mKyf<_O4lC2Im1i zc9Yi$7&GMGliW(zx0UO!L?IJ}Em%%oJ~YYO8!yZGC-7<4y;oW)^)No&QYmm#0lXKP zDx(;!oa$yGK1FbbWL;>*O6r4sUUEo;p@Bgj71Gp1va8639H?&N+?HY?3B7E%g;4e< zXb`v8-!y26=w2{Nu>Go|RGSJDTI9a#S8ieqYP6VKk4X zSca`e3~+lixz12h65mQMQ%=m{U};Y1q%D^j5)7T9+p6(hZ6%8S^faQ!f=9)DQJ^TF z!7Qcua!v2H1k=mPGK1?3K_JPELG~`l57^+Q{2R^C?t4YhzSy!bVwG6D%`dTKkWg_`(ps(;Pt za%2VUtQ6g-VqM{gxa66I+f&$fBOeH(uP37)SoPcl4KYdT=b$5r)%-wyeT4NgXw|=X6EFmZ#QaOHhHITQbuVT zC@otuPupX1l}&plAlbid45DLgqM95Ze+!L1bfnRNgkJ3(47Il<64RzVeR%ekIJmvN z_1`r!*Pxm-R*g{e^guI{bJx=MRKu75Z=0DY!rc+wU%f4sRzxgT9XoEE?}caYGv1<& zg!w``WW@zdY3(ZV%=W%D@Fq!AMDVuKj(k+2=g`HafS!&0wwo#%gAtA zkC}MR^mRXenxIX_9l!q8ik`pvleNb0ErUvAIPn;JpB43ww9W#wB0&&8G;c~gA3*vbi(-VO zp7J7F0C?2%(pLCPT%3T~!AyGni=Kv}l|fU8V^Hir@5(sQ19Ns0AkAuy`{C3CFnJd0 zpebqPv#S0;EL*8$o^TA_9jH;u5h4UnS{goMPdx>PKS9pr%{rx zC27C^{C3O|7>!_P-T#jAV#wFrT`TQUbp)Gc2&cKh`+fx8|3{lK^$>d!DJMu|9-L`> zlzqI!xKpkl<=ouI~qak>( zF;Du`ecrVGOmtIQQP<5OdiL7gW#HTBH3TG<$BN7&ISIoKX##h=Gjf(ozKZw1NODqN zBj0&?P7T3s{YkYb>_wUrj6TX#fzA~u4eXS*ADykX1xzisu8u-?4`3Vlpx;ZQS}k~% zCED&(80kwMnEB4E#YW=32!53*;LnWwA-=+Av-|jhmLD(oQ=e{47{Z29}-+2E`4}uke2YD4LNL6diwgBc)w8*OlS=*a*N(QTXg%* zj$w1}>PQniNS^&*>AG};s|XTT*Pf1|Tl`05)O$0;Kb`yEsT&a3?aCiA#{j0Ni5CR< ztEPqqQXO<9CveJk`Sx2geKS8Bc%qxykm?_@MD`{OCkMrinqwwa;7`p%Q1$q}hQ^3? zmzy&<4W4?D@z7s8(vK06pSi}OS4*hERI&)xLI(XAvm9Zu*xH6JUS8sS>N!m}ID>$- zGgad-;Dzd+0J`JN=`q*-80@7g8gLuW}3yt)(mQ{{8#Sa>Rwf9hBrJ?qZbNSVBP@p2cx<7BA$cso9lQ z94#^Utck7zzqOI?hmE}P|JBChGUq#BvD388;GtkKXu}xCqXmm7) zujO%Y0~U32XWqakDj|KC!Xz38Q-S`+&@&mgqsfV9^*xH?(l?du-)mH`gmjOqL)FHE z)OoO?qT=-)`+-OD8uFHC@FhqMv}gOOGwxzTN_-efR=!Yr1_sL5>szwBx0R$@-HAFA zA1{Bw(6-2C!j%vVXyjOI)IAwGof!iUEFm3=Ea0W!9*2yj(r=@ZT5>{yANnebIre~r z6QbovQ0H~}VIc`>P}mnzsV9^#P=+Q}t93Zd+Pj0TCYr&Jg*&g{WAO&|Ex8-T;TxbV zCV-?Y=dq*ss}F^F_Z-x?9&@PZ9!V<>t-NFz8GI{;7;v!cHCS1hlbVSJ0~PKe;lZv3 zZH*{RIIF7;jg8(kvBd1`I-w=A3%o|60~#apl7UG3tRhp2!^22pq;)A{Ac2zoz#;heBJXw>=_v7$jx5@#3iOUl^YL!-xbuxb4xX6c-Owi~Kb zbGXDcXVih{9w?~OFt8$H?3J6%7Xkd6S7*93i&N^iog^B}nuOJxt%d5G6L;C4!mP;4 zRp;px&GFjlW>my^Cl`APKk|nd@vyhvC8~qX4S(V=21!~|9PQ=ad-c+Aa z1uvRA<>f!Ui#0}g9?ZI_ob@4jM@G36knDxu_mA^Yn#n8#AgyHU=1#dr%vo`z7BDwN zgtHy`7f*ynkSPB?PoM;kh07266R7|WgC=;;@9}Pl;@nO43zD!xkfW3oq0a4H4^1d1 zkIb1W#p~wyB8}g(y#?CdN4}zk{g$r>2ZND+2urZM9pDEU`Az)ddngJB>hj@&B+lrk zX)LO@-*$}XBZ@TBw+&jCn2&6Y@4GGE)&k?>b$$y(-x(~FX=r%XJaxfqnXyDUGI6H; zJ1$M2_hW#ePJAN>W;aj-VUTj^Qx|L(e_I5B4WF@RgQ4MZw0}UGI=`nHJ>|bGZG;l6 z2hYCn|3Ib0PM*GJF^1vz9ZzdUE84a_6TR#);+^@AJ`qM@{cjp7(=0`0<&lmLK4iQ_ zK@G$AkixgbRdgcVB#(syZMrxO-kc+%MOSs5+);88R^@AN;g0c~U|Av3r`Q-cOf zxj%{9D`U8v+|jzQYe#NLCUi(^$gx@3owdqp3z0%9tQXb3hgA6)e^8=XXUn`*3bDoG zO>0Sn4%0MyGPFR28+jQ~=jKf_Xq+`G#cO#G4+KKvY?ebHMujy-t%dpLX1270Tfjpy z5Ek^EcyFu0oBhH0M|pMp?_MlhIBkO$ZDxe(&E4G{-dM9HU_Zb8x(65K17Llrsj2Ce za5v^y7Gi|hM-%DzI!>-Yo&`R^9x2_?SB$aSasm*&yz@Zqt+dy|L3$o*srdkqBNYKx zzgK!eX=y15^BH2^saF8g{B?oXPu`)JNI^r#WvqY`fS9~=1mz3#@O{o_a)$C{!eQZXX~B!6L4>He`{lqRNl34t7->sA zsqiGua=s9q$-eTFi*ZSabCI<*e^fJhV_#baUi=HFIBC8=vAYikQtvj(*=e6(l6j$A zLH6?}q04<$|Knk+%)r~Sb_~qXc#VZ11k(}CL?elX(3-@mZj7-F;DiyV<-1~ADA}-6 zX&N~@U|+tU{C`TGyjGqRE;xg7a1QknM%@Ex@l(_Zt<}3Y$-v{n(>fcEz=ypwRHhb& z$X>08t90 zi#Ay2k64Ybw$qZ{p*McVJzcu+(15p&hAZT1Vvp!L`20K|-n{ESPBzD$%}ERtjVrgB z4SWt>1<1pGQG12Z!l7{UA%cR7405YEh|GK*g47C?+d!!m9imP_Y6YRJkSZ0qH7Mk| ziYybo3cMsns*lSkH}j>JfZymjQRQm~o`l4*W&bw>$XNE{_@{jRCzoD_JibJV*cA)2 z*0@|p@U`t;IP}?u#`%@~^=s7g0rc<(XMeo3?YyaTrFD#1(I$l-6IRp~bluqBA8m4f z;0M)~O43a<6G4Mz)S2YuKt&ye2gntAFxdRY zqji71ZsG5_xOQh_N)8@Eh0qGbC>L;;6yEh&Ps0^;{*&F#Pk62T3Z;xQB{*zqP%T3| za-^eZ%87a-k1g1n5>F zj+To}Lw6F9rwM#6SX(GP>JKD`c$Oin;aGBHx+ar89(e3c!%49zqa8GI_s;8`u!H72 zs`;3CWe$o(APEfIj-W&=a#I-iadk_wml7g+pIPdtHRdP3ylP02F-={39qaNm;ZN2u z?mvPUkDxM{+kO_FJmUc*V{tf9y3V(jctG}o^>wxvT`v)iXKfL?EAY?D01Y_t96&wUJR zd`u$MjV_*)og_Cn{Dt>VQRByb7u?;UwXm)3#cc)ewuc=GMQwXb?>o&Y>dk8G?tQqS zMC$6}aerQBToAUKA5nj&m!wB`>I1fh3~d4Dcr<)D=FeaH_kWdKhDxj+3<*ladDof| zIX>Q;tx~tJrfcN{^FJ@=-@p7BlJR}U;4xikLOH^M3S7^+ga7{bKmTHdb*Oh5Inl`l z+tclz#R0P8XpGD}QC3E(!Qgc8J({b9nFiNT)ol+Eb&|cN;+r)qGF_amG|>)rr+Am$ zDSRhs1DVy}CisbGHf+-3L7svWUOoBGGyV5}i^;B%K|(tJu42`bH=wN_yDH|5*g^hu z=9>fKw|{e`=^de`3$U2nUEozaFJaOJjTH}w)yCzdTK&kn8q3qcBVvlSh6Z7{6lDK@ zcZW*BonWW*wDqNu3gq+QS2g~9=70W05pSnl83mVT5)`XwQO`qAYoJ7M`q6 z8N`T$D!|N$>B2ZSpzNQqe4|Zz{*yn?{NMkL15~oQmGCnew$h+aS+>zdpHl$|NeTp1 zH1-_7{`W`z_>0$5KK^}^1Ln(+-9nbdWX@DG`5^w}$*3Lnq0eFXYbfXIbS6V-WV z+;D-VWFc^qB?YlrhL8+#C}yH;?Xl}j#1>Qt=IVuOMfreUctz%i66JRwSdMt&q9e&Z zI7$nD#!QkjGhMQvePCku7)Zg?R2raup(g5BOj?DLd9V({Go6majbM>dzO6sncHxFV zZ|*SAA06Ft?3j3z(U~p$oSgnAajT8upWuF9)I76Hsz>zJ$pvZCj3a|-_9XGNX138LuXH4H2CW+7VNFW>c# zi0L;AXM>NHsWtGZ5|KaHyqqBVsf6&h~qmrs$NMz;r<2E2`f074Qg>g>BNlVaLTQcQ#h;I@r7< zycVsm$YwOFw_5)C!@stszs*Vix-{&mTwREv6|-;JhYsuI5hu!EVYbsducTPOCnPIL(rJ(bmK7?Xlgj7`xF>KGloyhKlI0d;0De+d-) z7Ew|wA^RS7X1g!&;g5ociDOyZK1Qmz=^yFar--y2k(U5oZe2b?)I5ruTlz@cfR>Ob z{aJW#?jf5*YSS`Dv?63BaW>BgP`&F^Plh1;i~dAj!on2&^#29XkbY=~ghags=%KA3 zm0jazIXmms8eyIa+7g1dD<=*h6QMKSHEeoL;Ufp$ipXAU4Bp;U$G~=AENTCQb{1?e z4HC56yWCa+db-jCTZER~qG#;&dYR*(Gd5%ojmK~rM_6&>o(IKRAjzW34}w~2I_N=7 zNu1Mu3Bc-VBrI{S*jZlu>g3Q|=#R&sm02-s8ypty+8F>T5Y z)#Gv6w=TPiEJnR{qBc7-7U+Qm`CD{@fJtH9&fnvuCmUA&GcH6%-+nWC3ZzHxwM9~5 zn=1B)x*3dDLkgwWaCT9FNat+ol#?<3WP?)T`F$%-sPGyeWLlQYK%PTuVzh0xrKXB5 z=_Yf5R?Jr(CV*~8k7S}2e4B3G8TSNpPFW36GABmdB zUfY4b=w%9?=(H(`xvqch^_&Fs$$JF_6uC3k(Bd=)wL@rv!hNqSx{X7lSmZSqI?M(B zrmQL|3nRxfCaF*FC2~p*UFE(cnVmy6y3_8HeMFU=&wjeGp5w|vTugU)I{=2$^`MlkBY$frl2)fJI)8Gv z^iHecnaoTw1%qbH%Dd-)w%W#EfDh5J;CED6B)ZAn1=%=6ZDr8ut~yRT`*87#?-nhs z&<2#5;aa2E2u!_gv?$h2Qo!xj+yj*N{Kp%CCnMyj^;}hZH1FP!OFNs5UtAKKt*w3{ z+#)ojM`dPX#ab@XzJ*(4^!LA;29RLyeGKmlZJja`=4+;`hPN#G*T8F6&)|{o6&J65 zu_r%3S|R-OR`g4!@6H0sJZ66ZjkQj9NJn8=pKfp>fm zS_!?=7_zXfPP88_-Z6*fweU!O40Jo4n~uuYV*ZUn(31o;2hHK`^w?@>fZE0$ zhl{_c^c0$nGSYx!DMj@5ZvJ{L)*3s#sJja7GXUDnp>4JX2MrhGOw}I71~&*t^~(PS zQU1Jr>LZ0`h%y#v@WY+o#ZC>K=McbAZO1Rd13`-Z3Qk9CwP+obH(a#IZ&`&XZiL|? z<j^Uhe@0RbUrRW;lmqDMr+sp8i8Sm^D*jgckf@(VvI-_kUgZdR2gO zzSBoXvuEbsbZ93!uT5JlIq%`OOSCNsaE1YpYsK7C=a|n757U7QTf>IC;$G)XCSP~R z^CK?_F#Pwp)yg?Fmv-1v3q#T&ss9x{J>(@Hz{X0tKuDd*!hc>wDC2!(M@Csql#xe{ zXXI&_rq-pM*^$-a?`;*ZNl80s3c8Av)(N{jw(~k#u4Cc0ymkpx>)XBFsX&Park%Gf z7Z0DE-D&3ZIBvQ}=_^OfX2twSTE3=q4;8^n#fA5{bHp}-riTuh-)Pl6&gf_==f{Nn zLrZ*hR^9K;L5tETUxhe==s+xOv;`99w8m&&5ej3QDLvP*^!~08c14X>eAc zH;Hec(o9xye!My^Ulg%qDTUM*kV{DRIL}N80H}g|GW1K%{4g%V z-G1B+xzpBJPtuzkw_VO|b^*T?N%3YvlNm^umWQeS#=LM^=Hf5m&*^4BL7bcsw6cXwHf*xD$75uTky>$&{)_ zPjF2IskM-eV9~X?iQsOpbGkw*{2N+)z5?KlPB?ZG2hJXt01_{_pVQ0`+b?Gp!Lv+? z4I|ofEp9!y>KX_mCcnmtf=9(P2#4%R^H>bi_1qZzueWzOFwjMCGZfkn5OEA6OF0Kt z9o(P^Dmgcm&7{W`+OobN460$TO%96~cn>8ad+>gZxP#~_C~wBhozkK!`X#xgtcqXs z&x8`{9p%n4U;$^0Fr}i<^@m-LC~c#F>K#SX+h1gefSJwWmN`3b@GZfW(c>>uOLXP3 z3$PWM5o@P#hw_{1QzP2x5=m(Tlj7=AqLbaijM<8uJA(MY=kS>{vs@pJa-`pqOQ3km zC6A!C7qspGj(fhVSm1i3c@b!}M60QBeAm|ioj9`_8w&U{Qf&KIpHg(CJ3i7uQp`XX zcL@~bfh_hSYcZx4AG{a4)*M0zm%!na*p5g0g$hpJjiTG253vouN?GeHkx%k0wxyCt zM(~*8M<j;fs*TSIrG3dE zi>uFVUCuFEDgTST_l~Oa+SmF-8Rh z3#dpHMVdfBnp8!KfU-rAE(l1G-n;N!bDQ(Mac|Eb?|8;@p7VSkdyI1u%`W%7?zQHc zbFMjm7m$Q6bZt-?T1OiXCRp3Nbi+HsXs*Pe*G6xS^|Xw#Gg)`gKV@-pt=7oiEtN47?ef-&c_0Adqpsd~w6=TaG|IJnsh zAdpDSV|?B?Gnh%$3sf{$sRe($mi8+;#93FyQP9h%2lp^UAEr{>4zeA>3!dWK9e7}$ zGp~0Nzpz0M(uHfN;uQy0{EN~haP+DTdu-w$!#>aC9wSk?7P(d0D8w3WYg;-W7quUK z^6jk5!LGW5>DsBPK$lXX<6s@{_|f$aZ%;yQl%*nO#nFWO*}slLuN$>~QStN275F}Q zu7n83mL9ODpaLu2uKR|+t4&Ws+=6r@Ng($!7is>eKom%toaV^U$=Q;tcYzR5I0c#y z#^A_`z*FC~{IS^d5}E?i!!2lIp@I?|$tUZN8e--mP_^0yC$;*m4f_o<+E4|#UzAj) z5N%koDp_m(Q4H5-HoTaMh)>uaYh6VOF2SYax}r(&9u|Ne0;kpWoGwx9zBn8?T@Aux zlX3vSC~pJM|T>Qi=OOA8F>wuF%&7WI8|sNHK|)2 zEwyZeX)c;@ z2P}AW`w3nL>8ijO9oXa`ifxIaH;ja5Q|{X8-zHD5-I6}E zXSCfa-32%#DfB2EOG&`PVF`HU$n-}K4(7=65FPiA8yrR`s|cAWIUWK~5~$H)5GzOj zEmn?JYiV>eHKPqkVENSHuvq{zlaA*|&JD$M_rBYAaEM9(7l8B2D)@mK068EoR5yvM zAh=Q&VoynGpa6PsCGp)}>eu#vykit;@Wa!%{<8jyhAhU9?H}MM0UEWE$>f{i7H~ zL{txqg=ZFU%$gJ3glKXLMvj5 zsgWylW!1waWTwNT(j+)Hi;y|w5P1W-N;-kZzgt&pb|8STCUCN>hAYWsq-L+cuJEVl zr{D^x8hed@FeAA~<9ElQss^|)P zTU3+@*5w@7m5MdLz3uIyni>+HAy3e;idEAJRU1r0I-nwi6*~GQ^A*DWK_%4(_lol& z7}|gR(-wq86b?`!D_>c9>JZWqi>RutN}GKS?w6;LEB{c=oCb5&b#tQxL&IB*v z%*e`lva)UHV9vpzJUMc+4~YHN_sIW~fSc^tAPHaIk&W6vYDlY)V4^r8+-U!YVkfGD zveD0CwZ=gd4qPHKYV?4q(z_HJ26agDoNKx}SToC!`o)dTq7p(I)omoK9KRr4J&784 zumk*yD;m@!Nea4e>KGt<1-Gc8{gc~3ABb~AxP!)BUgPreXoSdFFtyr1cF1=uM*j|h zU@0i27NI(jdR?e_1XYyOb4{J)BrMEw@ND~AN!G-G3rWuJQpW6uH*1vhyFbhtX^$ouH|Hu5#61M(M1_sk zt^OsVhKHyF#2gjy^HV!el|iCrhRO;N|A7B~#YHG74px3PwWWXwnrm03_$P3^x3OFbZCPznsLraB?yzA>mM2r=Gi@u)A@`iC z20)JCrxqluSSwpgs#!-J;Iv3}%+iJX*!>l`ILs0L_awZHI(t!ts5|qVHT&R+5}-8; zB7=VU4?T~+zQ2khH1m$M(5)F~785bc~IRK@KRY+Oy{&PeSFM$ST=Kmj(`&EhR2p9eoXJ*yA> z4D=kleGW%PrVTd}wf<=w)ozW@K3CU zc`E!9R;$h)rVok`?67OQzx3Y1t>y>-6mgJrhRzO};u^?2(6W?_c#ED1__^S}ELp`q zxdIl^p^P4VZgDNl)`}HGaV903a=VVx_U%eu$gdanQSB=@;^n>YQf#v!!L)#+qY59d z6?Ez&>2NBO{mqD)k6A0hdIkF<4O5}$g^n9z&Uv1WHxxzMIIsm#h+yvHh&HU;ftw#H zQ|Ks%8UcSZBKw1gYU5X)lqLBrudYMykmrPC#UEjLCh`3wl#24yee+WFP$4@+#uT9u_+pD&N34ir`KhYib1fL@bg< z)uDXJ0J_wuKwa^4KTI$=MMCG8$kXpSZCPxZ^zi|yF+GhV5g0gxH@Y|~La53q8uGpk zhp(W6=Fpi>(2x}VIHa(|d|P23kZ!knM+GP_B8{G)_vN-#J${4Gf%9*ZV;{-RbnH_x zkO(}S8uk*z6z51@Sb9F`^tey!mOGtAe?tyLcQsD6Rgbk*6YepO^Jxrve=dzskR2{$ z9j)<4qmE*pIxh_PP9`rT6AJs_lZM@Iyj709gj(o@5-ocR6>>0ww>a;vbNrvP$i`jn zGB)>h%8>%+_8I*eW)&XyN!2^b$E5VnyID^TVnJRCTX z0@EP;aYjU2IwDA#kBSmoLrHMt62jKB_vKU;D+Y?oMvf=Qh{qASTm$vA9O)>-YEqPV z7TKD<&f=yw^;%K4>U_1uAZ3uA9;eA;NFt-L%!24_g?+F!DansWCwceyj~QRi$}avF{Slwjq#iw z>3gm@v@6=(UVw9^2mDD-DPTASMY{4OKyqHoT@vB@#_yY>`L~4K%>av?T!MKrkU0xI zQ?9Gm@{;d^XP@?=k!o2cN10V7Zh+xX?(VtEcXqMnr4tdAJro;dCmr^B@hxM2fuW`i zUsXk(G$D$N?YpveH{AWC8W+@8ttZ?0{@bo!a=16%{^Mp!%H8O=7Xv zlyB=w^1lcV`fb6$ZcNe(5yytqdQI#78kJ?Zz3n@;6&xF`;$cvmJHsnHDoQwsS|xIq z&>$vNmHX7A*Mo2jDM*7)E{S^FaHkYzK*TT1j!^BF5#ZKaI^d`gI?gf)JMY`fFm0-2<3#b-i4}?ddg#`f|d|d zV+`Q}m)c@%cGOAmmHCo|xqnrB(!=;3NK|Fwfb7Vr2=rP4*1JelbgYBDRK}@0@X{o? z2I^~%v}$^_{D$~3$zaq)-4L(3Le1XAXv9C&c72!7WlAo8<@X|3^AmfHdL~nq8b{|eNGr!IWjAmP%L85Ce9!QZLn!7PsW<;gIUL5qzT$=#ZwMbq z{+X(pKlV;aUOl^e>HJ~VFK)oj>&EIl|f$JsT^x*0va7=oe+B@c9 z&4iWUVcm<4HV>v_WycW}1c-9;d!*?fCl*`Qq!+dph29t+bE|w?G-$PL5|v~zPDWz) zkT2O{R^^ksq)ptZj~HVlNAQ~wdHk+RB{$dM>PfQ@~xvS`r3cFd|!dtVN+#cYCl zac)$MDkRJp8xyFNSzQw+U#l!)E%)NnchT)n{t%ji-VdW=tqvjwj$g4&k_RbEDk*_o zQZ@CntI?iZ6J$gCTy_BhkF@V=Z{EUA~1^O)s{ zB&&c40UwfYf|0=n$IjR*53JdeJgnv!fap-Z1Z@j#*7#;(0U>pS%H?Q80D%J@s*DyY z)hE8o}s z*jJ4yemow;IH9Jvj!w=S`b7ZM*oxQosoT?#c$oRH+wIUF@IdUy2++ zD?kY85Bu+KBAtdNBvMd1%dJ-Z=ZURUb#GpDKp}3ZjEEMMw9T99ux4FrePKZE$Pgf+%onLqCtZpPLe;)ku+!d#0N^! z$Ice^0dwm4;VVBwdp!wCDV?#WSRCyqo27g3^)5&@#b)*WwDXwnx>?jVaNpogS-2=l zE7S^^nkX#&IDVJ?w_lmkuHsFinv`fm7=?~8!#5(7PvKJMMGFsgPVQl{2W+36pGYbN z3v8hRJA!hUoGvDK{g!uU4i?%LfOW80oTFevAN47W$D|QJSqQ;M%E6*+KL$}1aTV`D z^(?Cohky2TEEbom!hXSW`E>h-(e_T-$#9NQPl2v4P?)j?i8KMu(kSJ*O(U}YRJ_3#LC(>GA-lzfL5!XIi0N{$<+ zte^kbM}Oz4H)te1vX$fE$W`EQ>G@MQ@k^<%`KhvKGpxZVl4G`ow$vy|xkuzXMwYhO zQ`0oIHf;of@*PMrrEw#}X|G_*9y&3S5vDFvKqqW^*gMh@^9NbQrRot{PpDhT2fJk# zDnYnXt+443ripc~i({vC=Q^mM3A={#lzS`{sXC9Q^pLhkc52v&t9jpAQ8fjWmBC>{5g&Za-16`;)B4RH!R0L?-v|3 zYWKI6nd1zQG%!n>KVgBpAtb>uE}dJUL|*~#v9jej7x$fO^|&w0@lMk(m$M zECNt^x0Vs|kWy}Fd{BoF+oL_45@c04xnks%qsEzpvab-I4w4PAslT7h)N zZ&LR?yXWp?#VXnj5QUvLe>#~v6RqYyQlgY(9e~QO1;Wf7g1F)|dAv89I8)<4B4jNv zx~?qW`bQ@6GCWk>L>l&wRHh*Q?EeVZyTtteMm<3Zn>_p~AVWkgj^MiTc58T?t+ z(Hox#l_#5aEGRPC&z9wnwf+CuRGAQAp6C&p(Bg(kn3x6=(_ms6(B{ZE`Jc8ajPXiZ zxi8XLaN)$D&hGuAyYGJ2wr%m#@?VtS3spLKM?X5Xxc!Ogk>kNDep_~Z)$aY7{*!l~ z@En=hqpobTx98#e&t>6GUv1SxF&^ulzxY4&seSQh)*tSF%~#ujJPF5Juc9oB<1d$e z)dBqKNKafS`{+%KDEo*oCgv#nkjVY-UQ9)Wpx!d3whb^hq^0_&{w69pvfh^6xU^Ep znz&n!H^=&uaX)*NztcJ$k*{@Ix1dFzeY6<=#^GA}6339IUbRatsjIIjTX)mzC~=@B5nNW-fXe7!dYy__4wP( zk2n&2APNcnw^{MtLvarnj8XZW+kR$09Qrqo>crN=KBp%(bM_IL@H*H>gfZdAu@A|F zXUsk#j0uO6V@M_(PWB<0*z4IxgfS5ha}3FZQ^GzZ6HW>Hh)g&o93wK}lyD5mgj2#X zBoj^v`;bgHCF~oWq|a~2woG1{{LZu^O&~wfv9aMxA>50=YqIWamCdOnSA~JSy3FIQUG>Gf zKI8Aw{VU5|7w41=|NQrRpKAIadFvDBIDdag^zRei3Hyjlcqi;5^8YRGWK_FjuXf5q z=5}|@8P8Tc+kZeMHs<@p;}O#h1$nHVto>N4WR<0lmZ-N!?_~=aZt6PPS2XA0qPb5W zBpSWlEX2R)V8||&c|Sj$#=k;7{I_SzX3VhAkox$sXNlK{yV!V(Y<}a7L0yNE%5UR)Vp)Bqt7gZ)(O88=_I|_oQ7Kh58X>#DOUcH&wQDjibmgx0CzVI)!zIEdb!-ANL@IGC$sS;-R`gfdcCflS-#_`) zbBqHVuAc+juir;ht-+UO4s##rF{^5s2GFf1G&Qx5QzxW0z&GYY^|x~HgKF7()-~SP zCvzF(gXMmJ{}PEz;8<+PhemW!D}cge2lxmPl0n^mJVzH)x^TdSf z0)CNn&D;c?H9cd{_?g&n$4IO6&lBcocu#mP!d_+fzM5{?lgGZdkRV~TLANvv3mYLR z>JkkVl^h7`6$dh3ZX#)7^vx3ln0l41fj|lj4Gk5r5(WQ=(4N5GIBSj%mqY_16I03F z!OPOVlM5FCr%DX;AZ?%sbZo{zYpEr~CYps)1(?DK`NHW^W8gY_3Z9D-Couu={|URr z!|O13qCAy-^~VyNB6cmYScoRpY99RH!2=cz4NqCHMY`=J5Ms5Y?uZ^+<#Qb3PUU|# zD+)6hUAfMrxx*O zak(C&K0v%=gNyPbb+1dI6P&NH8twZ8X|BVpaK>6p$Sh}n2xpcZ_S9f7R;+O73)jp7 zC25)-_*F|W9m;j!E_pgSug*B#%HXfg_ zPdNA~6{g7%@*STG`~T0K2YdpYCLoYYSYwI1RQHaW7kwp~yby^SJT^d!l(0t`UKe@J z@!u3uAC7C!Jp~M378=_Z;@6eYmVMuNz&PQZWf#C*3P1@=Yos=9v{$dKV8`e%Vr8+; z4i$lM6G%q6hol&YPrJfu&+YZgErF}mWiDR4SQ}tG!PCm(z%<%NMHr z35vO^AO+^BUXy(*ak8YX#MuiL7?+nWpxTnST%jZetb!KJG(E+(x3iam26mwl zJ^XviIWDCioK+`h8*mY)F>|ggR=#PwOWFz(TIX?sU<<^*Ylg5>o#tYnOabbkCj_h@ z&2ge9)V#nX%0E4T)i90O$n@uhpqgy{X)9F{BhTmo>V1U)&^=$i%D;upSX&DLHbE&~ zz_$orAz(%9K1IU)0xC$i>bnnIKu5m z9FK{S_zYgGzZPmII&M_A8>W`O*4? zff+Ql@Bu)M=li8&ZRn(?3#tG}4G8PKQw^%s!$;2v>o}v_tmlL4KNs z>24peb+`dfnBn#82T1_~cC*1?4b*FDqz0ivS?tl9y^@}1FDfykAU5YNGaT|zGIlcP zOXe^i^9x!*Z6~n*{VQJ%6@#Ha3p?nJ0sPoapO1L7DpsY}8_jb4+TH<>$O}Z#Iv;W3 ztB2(!aPSw6#p9+iUfW~6UuYyDW4JB21jspVWBN%?$1E$Wg5G(~VP*BhVh7dLml9v; z^>+3vh~>s)y+6|XX5VzCZC|rzj6$d_eN^2%Ykn8@!ubV0XNy%xA>b=lFC4ZGJL^&= zy5^5qYpAMv)2Ag)Gg^q`Ks)oP2iV03#CiWpd=-p7UD(gr1L6< zMn#$R*NwS;!x+8Atn%kxsrVvggPDea>bo^Myya>2Mw>l-uyHEap7(Q5mN$Bzig^%V zQ{{zwxzp&2KOFBlBA`B};?`{&vya#HJKMKi5FId`#rS{)u?)P@mT62pW2OEkP%>(4 zLZvIh>+^5X>Yj~9bislc*CbYG5FRlAU_=f$C2K9mAMEiCjEs!b8rZsqb!9o+nh)Ov zdKp&BMqg+y+3qoHGzbJ<$U29~!5JxAuoyQnB?*PqlR|#dQuVD{pK-mkwhDX=yZC9B z*(9g#E~ENMJCvJ+1;fHEhFyc&%dV%Z8JGb&Tzmh?B_qb51_U@ zo`@_G_JDA^koXpx?hgbF8{%f`6<`FsLndLFI=C7&L%@EqJ`Gc^)UQ92kSCD6)X9B} ztzQ7}n8e>X=QY!L`cI7gQ@~^qlwwXhPGG^#lp?>jED@&ET!JjX2by01WT(0Ln>}0j zOx~WxTp-aU{t;;aUQB^Y8QaAh6&B_U;qRF0SwZlKbMQ#gdb`5H!b0FaKEYIuU}l?~ z^YDRAI8R!Ro{N2z!NF~IL+V}W;h~|I%m3wMDK>sOey=RfNLkiheif4_tx0m;CD;!$dPI$i@3%%j9)C4j z?OFaP!W$4$0HB2kO($kW66eqsn2qVbt9%CAq@PU--r#C5r(rIw|4JM93l(Q!X+k01kTr~Ad;@O;Z+C*E_DSbI}gQ$uVE ztPf#=30AYyoBdK=U4s*K)f7<~pWcy3HI?F}@gIg%WAs#G052kvuU77Lz6%y4PEUBS zw)PfwM7;A`reVUzpC#RnP|^ zDvH?%B>q~L{qD|eBNrg^>hi7AnALC>VyecWCVvl0z`-P!(BJ@-Z!oJ$$#{%Cl(ea| z+@a6TtDJF+PJDb;qrf?!~N0ik07HC+mi+v+RV z0*O;x3)oo7)-nLP##G4yY?ILg+Izyo0X?`w!nN?S9iaU2=u{-cle%uDkMfbfd;cZ- zeKj>7f{2N&$YslYB zZ=}eGd{@%AH4tIH%t5FsFb-`aeHMgp@YA5Bh_7XNRI|iCT+I+uHUd?00pZ!C5K~^T zAcqlGl9Om&cYQN^1nM+So|P7uu?MZkkUh9rUj&@`njU%=s`nWs!^Azk!%THAC=`DET0Gu92K;?Dj_eDF=i1@(mb9I1In>&ju}B zRqPD}M-t%-ll053a`sj>MjH4dPRc3+P78+V zrQZMqwCEjNkJsg2*;2KD^ptWRgN`agnF7vzC14ZHnK1DFSWN6w4e(H|AOLJNde=j+ zbz~E!>j^#na2=%AijpKf*mDwW$#9GWxiso*PWm3mutg>{hh4WI z-_ftM6Ww#tw^Qa7xFP}|W@W^=F{Bxm5EH-VMZnMJ75_*)c7rwnm;O{B`f8zwwy%_9 z9O8Y*M=e1{i@<=>^(e)KiPJ@7+I7++gs6fFqy*%W4vJaj3npZ4t}yQL^iy9$*gT?{ z78Vs965E2XJQ0Z58}S^EO;A}KIiF=KYJpaEg(E769lE^r1#O6 zB88YN?De-TIdrE?ZsGnSvPoP1k?u=pSjWwhZ;FcMVJ2U}*k=PuznD@Y;BAp-dJkao zn*u6xphJI&LSvWqN?0O-(CLQ?-)*geouX7l?&E=KM_G#Nf4?$T2RAOh#OyHBhdGV%FCKZNOyoK)w3J#u?Y8P-#Hj|XITk|v1Fp50fnax zu_~QZJ^cO#`{CXNVyBL)fOpLHVy(C%^rkKd1!WZ~@T%WzbcAMz3q?n*oOHC~J*+et zNeu0i{d8uxpOpA;e_O>0W-HdkZm*4eet1F@Wp5p9!R2IW~{^(1teeoINL z1CFo)7_Sf2lQ$Eu61>`I#7;T%<>{aLU9{cASN$=I*Dt>1{nPXJ;B^@@Wg+%c2h6=t zMGyPXEQ5#+!z2%46N{iXfrKsl6*1DLU}b@{`toRXqRdVEw1=;a7?+O}MO~8d)cE4@ z2Au!=yh~km{DocW{KO*Zot9&XYfK}0zs$WZ-F7&Lo8y-l0oq^1R{a}~?ZT2?vnnrQ z4<{@&SDo>2>Cum(PF$~J2UAO}2QFL?`DEL_uYAhiuROpWi5dJwsp7XrQ?vi9dbT!o z%Bx?B2MnFF_;izwoZVCQMSpTYYIqnj=p3&z0@(gIUK7WdthvWFE=vCwXM=AkTZzz9 z)bLpKkCKj+hol}2rXH;hx1ORexv0u9{q>hY+nEKQAA|QJJNe3UR@ybsvy+8gvv#(8 zc?q$rSw`r~zr-4GlmC(|#(IO(zwy{NjFsYMFkT%wJ65fn6EM3vw0DtQpwPEdkDU8r z9y(bf5r@k=zV2+fO-|EhJ8Sl(uWu2ymLms`Pb~c^741Ls6#ekozs!vO%wiLL*8y2| z)sLe33OC2X)4qK0a^ZiSq5U|!!!c=d_L%=ItmD5wU%89>>6nJ&)pQg)1;^{(ek4a% zV3MV-=jQrUvgpvcndR4ht>_Es{lfR$Kl?8mqpIKegJ`r-dbwiM7yH4hhQo)?eepM+ zZ%l&gzq$-3j2q|Fk(($d;qVU`jAj4E-F~8);D6Y3O_V-Ols<9nof8(||H8kW*jOhv zR*n@k5jIZvtDHH83A;LBS0`$UIgI5*O);w?1Z5CLMXwS=Pm0?Og$V~5=&0w($PEd8 zQnn9L{$k9U)_+cTY_6+I>MM-Od?C6|;8M^ViJ;am&){D99r_PVCj9e$RYhwq-Hm2i z&5EEL8E3wP(e@jXV zrmxrD{ND2h$^v;a`1Cg%(kd!;zj@O0lIXD~mljmKpW+%A9xL>n_=&kILl)0GaJ}}- zOTQDpD|vJM`SUOLckDQN?H8k@7yA`YCdKW}R|t;xUDIZlY8&Hi{7a+FNLN}(k7dJv zWL;{9RLR?skR_e$N97ZCf5Prh*nRdj%J?ta{lmM;D1+W-VQjsrznAw)$&_rZ&L&Cb ztbGez)T4NFHD|mR=*+%Ww;`&P@5^6b#ZUg}?I-jpodKy&>EmYt)5mz-2bsb9Iwcw0 zsh>&)2V!o0`JvkZck)|)q5isn&+md=tNC=b?QN@^y=+ll?EoJieH|XF^&gzX&P|4 zv;XDiH<4)k8sL2*CpwW6{c^eDPyfy%%!yNxiBl1d3CQ@rIrE>$iB9B1IcCsAPIMwC z%IQQ-#Kse`@kDIQVJs(NtN-buu8GQsiOL9$W%<8m~Ctm7*dFJh(0*4WNm75&a^ z=FL?0ojHZd(&d(Z8u;~IbgN%*zQ&hl3n=KlyUFuw)u}iB$E#BXb#NlE{@Tm&i}BXI z#f}2DGcQpW`vx(wLAPqX7q@3~7nrO&v6AiKt`dQJ<|JWgV&J^1;pJL}L&Y#>bKGe@XZur*SUl)g(FenoSg>BAF6kATD6(*A46G?CmvoOI# zoHzyf+9}9eB{&?XBtX~UNw#crPPHA#zP3`K7SNi);$kl9Bz1Fh8+R)1^R1~jxMez?A?cC?2nY{St4xk>__P{AUm%!n~TnYz$A zDQ{(E<%+MiO?doRTxRg;tZeSI&I^OJ-02JaM?b8dyTwGnaeSm&yDs}m7uw=w#s+g_ zvw`!P-of;h+#&3lj>yD(6m8Z-yXm=0W$ zmaY7*Kvdz!v)z_T)&|RdG7wOS`H3)0EhSO=$`d!Vq7U9QZK$ECsp*oLLyvbf;0a+} z;g>Frjg6Ip24?P5+Cf=mNa(nf; znhV@oSUi@KcSeX^a2>MXJtQgu-3F7v4)BE1>nKNc&f=W<- zG}+QbJx%i_+RjA)r&W-8F<|E@nh)7!}j*}qw&TnOC-*Ov_E41w%fPq z>J~7i2h=LhyxFsQe)_SbGX|sGx6)lrD&plG9UW)Pnx%k$5NskEOoPG9)iMYA7JDl4 znI?UFP?~hsNU&J=Ep@x2{Wt1dRBEa;I3OLF4KKax&{VIQ0tSaU$ZDmJ^!AOmTcx{D zr@cAY1xG#=pl!GgD6w;bN7xHd3cLMfqzl9weVSTZUBjc&BnamwZ)Rrp zP$M;|xspI)?LZkU`m}~)EO*mb{zDe4Ffbt@p|b!qyM?L=rdODD4I-=iUh3rE%<5sv z@+zyD%h#kWTd4cnsQBUKnTt1P z5)YuG$~uDuXu5-aY+Rgg@c@N%M83M0nrdGuxpL)7 zbF}K?@AF^3c{7)I05!g#qX`nB?TufTXOtbE>ErsF8Imo<5iY)xR(}06*oSx(FARcW zA7Ki?@tNK+3UlUGP*5OpM+88@L^OL(?;x~>Wk;3$c8iVg(2IW{^ia06zpSIfXp@4h zM4dSCh63;1)2{4M44MN#+rUC5`;tB0O|XYUSh5==BqZ90fKaL>?j4 zw9d;0b;nPA1YI#_Ui{+^t!IDC6!m6bMNcwUIZc)`@B{U*G{LMov5{atQTA;{qP{lI zW^UPLWYm(_2rws+M&F*%m9BIg|8$`dfKP3J=HhgFKq%q@!{XV(Hnsp8s7M96e9WlF zopwXr^&2on?-UrDnYl9^$9gVUg|!`y|90vRnf_zF!5NYuZ-fk0dahIE@{`5lEv&BA z?0Nk6(V&0dEd{&g(QtXSAHS4lnw^b$!;z78-m%V$NiQNi!S>u1q_l8g6cj{PfSMjQ zSjdjn%*vU}t+u{>2qs->=)(sm7Ox9YT*}N6MI7e%Lv4 z#y*X-Y)s~gH!f4M)`+r_^fFU+xx+pkanla!Ra_vq0hp+~|=O@Y=-+p2@s zdYGscK+QL#RFv}Le?w$3X#Wqq;GMCX!jlo9G^K)$4${W@)YFKSn#5jUG zI&nMiq4L~Nf^uM z0TC-GvS*)ioRv3LPHcR9v5A9`k8ZtTBK_mKo@hBO?ABi4!~ca}Sy@lM1qq zcZ*}ieg@V6M0oby-G~T5f@)$jud51;*IO9Y){Ctwxn1&w_m-&6l-bo0ySL^{sq^HH z_eh|b+YrBn7m_Bu%|)3RM({^suenAP`NIy!`<{H1Lz z^LW6z5}I-RaK)n;S2``<>Taq{_6&`&(sWfz4{H@&#>M4x<-WSAm)8Px-=Wj9j3XYAffcLUe&Xw49avP%88#VvGeY~rNzU;V?(Dn7M>$UoB3 z(m^kIBM;R^ZQUC5aw?))1x)9)9IIN&;H@dFR+=q8+lppRtgn(cky+33pO+@pulX^qBK&^#Tu0e zO>ij)V6vdjZt`H2JN_JBzbIatMmVB=*r zg|RlkCj~F#EtBN3x+#45W5zS#*zXH$ z?Y={A;A`B#_J$YV%~xjshVHSW9=Ar6Z?-EKw7zA%!ERrmanGH#pQIGN_7e=b6`nEg z7>xP*d?n7#$=+g8?r}|KvuWk+^?nbT#{4tg)m7{&+13YxUtIuGziyGJp~w0(5JX*j zuRYuwBlGC)CMipuE_05Veo>6dw75b9JC^*n3vxOFg8Z+^_3_D+w6BepqH)}^C0H* zSx-RD5D*%V+48%Ak^7Tt@QAs~jlZwr4((GfjKVAh!Vs*}1&QAKwHAM_9&G1`TmO z>E1u_q#uGNrixW?@5vy3`E~qrgFKHl(FjhysS`CUx;CN7D z01=Dg0$Jd!4{C^b65t7wWHygW_bJ}| z*Y^CoQooshet=IYwoUZD;5$yvS?V2h{ygH%E0i!*B+rn)2cslsqE9w** zQSEaWZ+ihXyKI96Jb}xH@>qioAHoI1-JhfgPv}t$ zR`=Ao+29gBS4tmUyMi#GI~F+N*8tWZRLE;4W4!@T@Hu@P;X!o_O|&Fn`sQZ)%k*t$gxD@`oF1*I2Pv&=>BG% zKK6CMUrh@tTnH|>KA>U0cHxUNUv8Wwd zpK81?7sQa6P_8|OpCHtpK#dn@ghFeTBh_k;vH#AY8D!)R)2c9vO5I%JMd)-w=goO* zg9KEqm!98FYu)EpmmdJScFtQBAk98}`0&$cM@jnF*~^A@Er8^kZ^8&Y8Ke{$#V(bV zm0BgGY4)~i)-@Mc_sPT?_#Jxh@y<_XkFFW>-D$zU19&qK>+^di@ytPERC+kdYuEWR z$w2J3et<==liS>L^V#O2%M?!6cp=Z0#dk|>PO?>p(CIPQi7ep#h_U-U^BuAW2ZHzd zu4rTr76WQo&n*0K_z{W&zh{EszQGNvjR2_EP7{4`uUO6Rl(@^VAbM;O*!?>m?>di| zYU2;UD*moV+&aR{VfNg8WrGFIi@(yP%+dEuz-ak|(nq_0X=Km6v8)I`AHnU&kIxXH zU>6b*qC$vu2f=2WkDhrtnL87(`okamcAba6lY@89$5Yy2-450_ zLDO@f44_>RZ!+1h0vReI;1>e%If!J_o%MRyr|Lq3{haYh?rb-fT_ZYzmYuY11I-4&7CcI?YqNUjpJ`}QUSr5AqE$@@iT zK|sQ~H9AM>EO!h`b^*I^md-8po1K+0I{CGPVx(E;Cf#4!1TJf9T#3fDTerBwm80AM zb

    =s=FaHW2E@c6VGY`&i|m8rOtkl0l(3nt#l6?;Rf9ox0?VU7f?^y^l=E=OZE#d z5p>z$InsX$0k|;Q!h?$fPAvc=&B26SEu9R!wl%nx1mtxO1UX-oN0y(}vF%8Kqy}MwVJH5`SgeBU)UIaEx?&*u&7`4k@FnK$ES}lxM z#U6&-&)Z!W%%%(na>+57kMC?Un1gI73sODVx8AI)HmeX%^>;nGgTcNiN8fm!6)T9@ zzx9t9mv3o0i1Q86M4ctoDHp3>xPotp{mF!suGK;0zvJYqfS;owI$}LobMc;bj$2k9 zp*!hMltwpDcQ|~9zCh#L#~>0)A#-p5yZVa85I}I%&J@a%hieV$zaU#A2&1iYx*M;U zci%DQ6+`j`p>h=q?+o)aN7KIVFs)pqH0Hz^zIBE}w;hFvO=kT*3oy=z*HdEEq? z%n}cJHcM9O&Mq`~SN#;6yvj`R8_V9T+H+?f;f9UNV)*!mpjEMGmUQp_B{m|+Uf4^d00HizBr>*u=~Bw+4xbi49)kF}4pP8z@|Uo#%E7@D!y0mx;;oo^ z<=L}E^a@-NFlX&^9DV~?lc|9~a<9q96u)W!ysP7AtDfvGl{7-w!yT@>scL!|#_+B1 zZE=pLTUZ?Qv%vmj4%?ay%IWmJ zEDW>LYoVo&$S6J#R7+e>o--XaMaw)RHzp8A6dM4T-E@XVS6XKJH;m^qWTS9fKh72x z7bmkt`^ysCF$p0lAZ!T)82bQn2js}m#3sW*fLx&P{47LG>iQXM;<4A69gHu&|O@Sx( zRO9EVJ{pY7YIbxE&tgr7A)U+J0&pi0UMwcUK3(0K<{#*dPR>sdBr*ar3}J$O&b;19 zUW0RA8X@yyJsz~5o`qm>q%LpRBe6cGZFXV46b35KU=-V%|J{kYLj^~2D|ODo-2+X{UK zPqTcb!%NW;I%x~U5Uy#4#a|`~$9V~ScURn(0{G~GU|AR9DTUxiw{4rjD^q!k2Tw$) zkv4QhrXA+?pwYYC(m1!qO`LG84YSt4y%dOfZp^Z^2OMyb#$Hl3r(4V8Sb=&79GhPo0So52rFJ@(r-Xu- z1ya_h7D>XxcwU`xVfsCCtn5enJJg~uZYfw9=9o9DUy{KW!|8k-xZh(1eLg-uxADDp zIE3G4z{FGXYIwAa;q;>O1_?;<6~X>DRi}^hlq8r`aCM}QJ8((B3i5kFeri2Y!{-R6 zJ73a=z#ARU4rOn@vgmN_C_Y(sI&-$dHmt!p4mEgq0h^L=B0O-^Vg77$oN3KB96G#h z8+MU8I*;Kg>FMsS?XNHsHg~6we!c?a0x+zTyH%a$&+fv|>my^SE-M}*Yz=9afy64f(TSjgJA1ScpL_0Ju@mleqvt47UMh`ChwU|IZSYsWL0E=w za-P3#BYl(H$2V|(_1}iQVx(4f6Z-hWKm%47pTS?Uq{9rvnTzehH?9O9IaUuaMjEX4O0(*H_jJA5d62 zzpwf2x+4XOo@=vquJ)Y=KWoq`(h+CW(b(WH%jKiHKabi5)~E#)!R-IIQo+rY4n`<; zFNL(nuX@l1TY@mi?Z#e-HIM;|MP-S?6iL1nT2Rj$(&#|g@Q36i*uH0#hvJcQKILwN z1i?PKCMV~d*T?Z>Ti5InDI>W7=1Cf+Nqx}pN74f zzt!N5FXp)b@;eNEZmQ{k0URlDq^%bViZ5jkJOSN1>jIIA5Q3P|BJj`odrdG+SOQ|= z77dvl4g<*~Fj)J?V$(|&1>&o(_;yE-0mqqoQcfQ|5PavOSF;(6*4)aIrg;y@1x2}t zw{&C&u12K^;qcliyUw#rQ*6OW{5Er7_{7DfM*>suVDbls5$Fige3*9VnBHn^k@;l# z@_Y~~w{-=z!I#oYj+ldCVAkSb&i(tjt31R?z(udCv|zGge~|X0OA+fHe6@V23~?ga zUHE9-zAf1Ipb9X$Vwd_P#{NPt@9QF4bc(S#`*!r<$WV_~%5(pqr#Giyu*$SodQ)W8v@!lM?sRPz|E^gF?vJM4eb*>%_tF+oHA5);Vo@5N9wJ0bd+WvIb zW#0By%&zosplSg+939H0Y*-Ce*^}4rGydEJ(UKk9A}Xgp`sCt#Ffk_%svFq@X#=Tq z>Y@B}8nPX$u#b=Xwjf+sdq4vdIpoFO+#wuY2aJ-fa8ha`i9*aYz2qyg=Mc%%!G+pe z5w+GBWitg5Ef1+qrZy}*QgPQ6=i@kEh=!9x4jM$OR#Mtz0rG<|utx5WU{rTj!uqN- z&B6S9UU}$r!-L-8dSOSe4&j+7jgrN=+>I_+Wvuv_LJMgUI9*?V!*jQUZVcb9o2}-k z;?TWzKpzm6-u({(pyo0nT zC~B{TA=qma5o3ym8jsn4=OVlOpxnNG$MLEN~MNpQM@`oR%C4Rl+{CRHf-3 zxa#cJ@2@|iQRNpBQOONDw@UT#>3W_LtZe@|je`743IOo<3K7M76k+{!KW{yBT88-z zgTI0mB4T_=S%Xe#?~Zl%Q$m4?OAxcqrbkE_F+3rb_8*BskZXHi9C2T5Oumo{oU#Xj z_G8bMUtB^(3|8=VGrQN}2`{%eSQZbzlpMou`!U)=DBsJK^bM|n@6E3B{-_3sEykka z-<4Os`zE{(Xx)|9or8z4%VUd*pIO?_DjG$nn9zqWVdYNxAqT~{I(hMC<4aI(*#cMT zT4yD64Y)~{jS&70-8cLxCvp#u`#_MQCnd94YMn?hs-z(9!0owqVKkoA;TL;!Dll}lShjB9`FG4Vf|;1HRALiNa=9Tbvhi~L z9$8)j>Ex3m>$S$RVEhoA54$Kc1iqiIJy@WCb?s()-!k^M)7++1mT|(hS5ymhG*59x8_QS*XD`zg+bcsrdNX65(f&+(#AbbPF1JR50HE=N?E zX_H`}xh(d2rG^I`SS5C*iVfuBQ9s8}_Ftlp3gxz#_t*9D0LkUA!nvDqf>J<+&`gJq zLANM~xR5v0_WC_kv_wcL;~S=Z@}@zg!G)EW;}L-cNZ`n6>BjWFJUQ9^V~V0@kzHI! zz~rO3X{|pn81sC~)`^bCy>YRT1$|T_(7Xog9^;HO< zt;dEMT}JuZ&ECTMr`VAtv4K$-@+*66p+{4?3zLp3eEYV{6q_EZ$IMEz4--X7iq8fs zkt=;LOL&eSviB}sbvZd(XGzb)HeI=L?CDhAOjreOibE)w%V&>?dg8YCw46#08ItuG z9WF^P*kVa)DEqKjVh1oWWVgxQuw5#9lY1N0m?Y8$XVSC z*^8~djvfo1Tl^Cdk};}0TcS=U3Ul6uT|&G;Lb-*IP^2X93>+cQ{@dX*gvC%tdnS2i zyAb;CKXBXI8MkxJaVTH27G@u}L#3Bg%s0adE?BjW!Ks>)hO{{MN8iq!w%S)j1#@!x zi(Ats*N0*+RP6qiAt!&_a_tg&W*)*0+hJmq+K^;D;d44~O?~aI%GtANQe%S%e=oJrm3$zY^<^7XMh-{kt@1EhDn9X4y=68U;vIBV3q3;VZrj>W+)FK;D1 zC=-*Yu$^Ps2!(P~2}cIxiR`T@>HXzK(WqJ)#C>QX{$R<;kQmd(A)Q}$3(ws9A&Xnn z73=L*2x1a zO@%YMCKqKx^`2#+gLZzC8T(~ezU9J=B)kG2_SY_)*=0h8=JxA5q57RS{IEEM^2L@?l^^XjO$IlH9Vzz7{1F!Y;vmIPB0)8<@9{OL-Nq-#f1-6~TH1o7=gVbKUCO`*2z_?{WGlYAPkFTWI~t zWgXzrB`1zaGyM5gvSTMkKit^ohTnS-tBcDdMWEf*t5h`S?Zs%;qG;`1-7o^rI;cxB ze^J2j_22f+yNOA(o+S~CXoDx36v>`nTX_#>v?@(A;C%&m$J3@AAh1SQL;iJrH9H;Oz++Zm-?y->+=3cVI6D27dw5dc~w4YU`k_)A}%34b*H0G zIDK^EGdRq0WhYJ7N>VA&r;!17JdF0)Ew})Z^}-N)UMRrtj>gc>FSE_d-(8HLM>>*Z zH>-K2)`Vfkafk8XHXJ-}m3@yG5eG6DkmIPY>q5R@mDC#?m&F#fBZ-$Whzq$Mqj@U4 zi84eTeMktLB(Id;7W*eFg-9thRCAiTm%=_Rp)D9gpp&)%?`ag63E%p;U-!lvXPaJ~ za5q8>(}iPj?lnwDRa9@HmN;xbPTick!&6?+HhPog0!%`|?9K&dNNUm^B^U=a#<^G& zyR3Dai$V=T)eCX(CRp8qN1uBNPm=XC{ut_MRH#m~*G3d%oB0FIMaT)IP4C|lA z2Y(M#iPn-&D_M+!2Uc9xx&d(+X#=4Y^TWujU*U3cQ;HLr#glhMFaYEK9Dq$X>=BqF ztoV?7!=*_1gqb7U+FSg>v=lvp|NanAboG55oS9R{T2|+GxKxIcoG45+&c^fqdAEuQ zQE9_#N%|3ycg4zL98WCRVyZ?S4(o2Sj{oJn;BFD_k{OncbYChNA7a9nY7fT%YUXbI z&`^_e`?i>#`BYo z)U90T-27|V{*re%Xn2y_i)w|xO zQUx*4WjLr`Li%bx$Qha=w8NE#}~U z)Sz|S)t=nZuvn7RD-jFQv7|>44)0w8v74^R#mPtg0hMEmDsi$A+kaq@G__rz&*XPK zSW>T!KH3V%o#~9nft4Ex+?RgB4e=e+(2K#J_{rNuA3%Xsq&p51shWK7Q-`B}Z*|%` zY(=E&-C>@JG|q;T&#(S6fP8_6ef?QW=T4O$8FH&w8>2xyrtj}vWFYUm+h-LpeRaHmVy>cMHkA@VihjMzTu2t;T!c##(&D8`dx&X zc|{m)r;RT$5&{}2iBC$AxY0&G#F}vACxlty=_G9G2{6|wPbO#NrXa}Z%3Bxplaxho zp~A>(${ruE@y(?~D-J_>10fm%C#ooimV^*8Nf% zVHQjCqij;e(#)`iSrB=x5F;`Fqf6ywYT)@OEb>z~1q4)J zoQe7Cn-NLtP=r*0=e_vLcrZm&+|_*vkEWZ}`3CYq(FrqyI#LQx)Aglvvq*ChP9 zW2Ccz5!`?tN#?YLyr#Fy?joc6&R%uL?c0=MaL%5N zl9ooNj7D@5=lVsy#_`_43iB95Qzwd{CD9>&OvLoCg}l8bD#E}7S!U8W(`D%zIDrHA zs;!Nfx_$73KylSeBPn$7_g{F%;RilHkL827v}!QUQZ~;O+Venw95X;690H(e4z`Mn z;kvg^boxzxea5$K0dNQ9GC3?yF=AW+V58k{kSw=$#6~8r)6`>{oo~ME9CaGt3ZyBK z2_C954jzd`3B~W<6l=ka6H|>O8SoOPZK;h{Otgsq5(=Mv@-4rPjI@pkJCTq2dtBiR z7qBOnm!se3(;OeI#_d%a<$=;;+HZOF(v+|mUk)6%&AKN#8_JLDV*q?<%55HX+!D(W+<>E4 zQ}(B`pj1BlKJw`$JiE05&48&DR{Ma@aepR{L`#7+o949uIo7Xs_U2vQ;31)C`-n6( zQ(E-qMJ2&-O)>{-S&15FvJ#zneu=NnKFAt(P4(FY>Q3S0pMLGlCHTO(kmQ^Gi=?tY>|LM1iD_Ejc@IY;zooDWI^Ex#C)TI6F@6ZPLUk#yI5Gi zKL5k|@jkbS7Xr46yYMS&+%dsgE`_63JUA77AH{C3zK*1Q752vehA)7n;t0;nfBj!T zLPR~wZ4}9VwFP<|x+mXf;PJj#P)7f_{anzIzJIfzOQwA$)I>WpvgGf!PF2{tZ~jK1 z$sPXFmG8C{w!&8YV^oW56Dlo7+zOY(D+tefQO9O#re8;$vqX5Of3fx1qMCLZc{5yx z-jw%Zo`tFDtT&35N3xLe`UXEpFqh*kPr8S_P*DDxr4AI3@9SItR*Bgx?}30YTfKV> z*Vv6^9Ob;>>s|*giMm#5GBcYICqlzh>6giDhK(*ZJQWLIu#~$*%=@!lau;M9gT_IK z=vT{Ui~~Bcu&%EN(c2L+^<%G((E{_bp*3ukLw95NL+jSxR)B)0IveMq$q zna$vT2f>wTu!xa#s%O_Wrd+@Be?&8{%Cg3a9O_K8NVEL3kr=04}<3F!~&M zx>r%DfTw5C**gcJc(C-Mb>_w^cmf0e=1Log_SFm#t{leNIiAmR$HWGsB)y=&p16}I z5P$(OgYcyKxhU`S*W+zj-1?h~cSzI&x+5f#U|im7y&<48zUb=%T@X|(CPt^Q3I@29j-Xw)O)5hbs9kqm?l4+5p+LqX!j-L) z6sS|7n`iD07WmY&5sT*YFsCUxt^PQMJF#=bte;ibY@fR}dl^F;0yvX}(l|yOh4*={ zq*LT%`2b-s5Iew*9Z`1v0edtMoswPBiXx>q7jW)xj?;?&g@+B`#UxFu!T20uy_cQy z9mg9Sus6Cr7xB_eLnJ*i+tsDiBL;!qF7BiWf;~lIHS(*GgKlbY$DW<;)DBKp`&SHj zbUrWr;d}mSKn1l+@i(@r^T$j6!uX$=AgY=Utwb*mbKnF@L6e@nE6ecZSEwB#!pTa% zSSEytvgBDm-dI6XMOtLHzUwBuNyc)#eP*@M*4LRc*X>A~FM8 zB{Is?0?O;)Er_zxPQ5R(lD=^5E&lgXJ9lX4j$Zl^pXap@K3?aFO4x4UW8@#Ag6n1f z?}9pP^K}_v*Kb03;{@fxqt$YBm>_+{frCEqkLaN>Ej=SS^)Dg64y_3rjxyZ%(H|fSb+mkL#Fz8!qp(zTn4l9`4U;tvY>?xChGP1Ye`tocey72 zSL-lWBtsIdWt=ch1?Z7K(I?cxj_B>EVnc~$c<TZldd@_W-Cn0a>6L zlCDah*4k@OC)y+w<9EwhC$ApXXXu(~L!q^0oi4b@z@*+<yy(+&?i0A7%#tX?*~@DCl+0vNZE8sN(zDH1OP1ePTFWevmpU+jo?8J6hTspQrG1+Tbv^#=_e-Ph+2Z9 z5f)xrvzQFiM7PqR`~jfmX@{Whz7fP5A&&-#v-;_^)+C_vF$8n|ra1W;oP~}MTh{{n z%7)H(I7rkWa5;tqU?|20tPhjYkHSJ^_;x&mK;&G>27P@Ng?5GsCkv`r8SE*SV8$M$_*b$2bqW+v4E!yEgJj=?u~UIS5zM`}iUv0H zKLJ0Z+P8maP68sa#RJW(X#Z*}pABrZ-`)8oWapWviKXby0rn-%8aS*Hn8%6+pgj3g zTV4}=qfA5bMq4a}ZgPgf{`eOYAzrnu-lpB*0vTNZ#acvBm!szyoT{A0T?G#A2uk%V zhVD34xnTK+`nJ}(=P4e|ayc3;1t`;WaYV;O)bO4FlN?d{)RHO$P!t$-7$}Ga+CvI# z%Xj|R&uH`Rjtq<-;?Jzx&SI5}f}1O(aPA##O@eg?=i1(+ixUf}kL=7I_`oevc}Vpt`W_n!6f~ft7@%ec>FnWhj<|uO zBtpS-G2lV9Q$X2d)zUw@;g|+e*vlFu@^-@;$_zt>52M}}3KO}dH%`m8KdOrD zXsrUT_f7d}-?o)`b))MbLda1(8Z}@{^g0XcaLkAjT0-=c**L{}Lw#s&09l-aP-Kxx zV(f&<5R9bf2W~}i$vMW#*+05jO{WcM$h(#VIeI`Jh5*Dbi26wz<72V+6%E#`U-d6HTDyWMnX|Z$HZ6P5F8qtzmkrVGY`Tc*i6q z46NE;nD)Wz&4mZ$Nq0?dRA;Jmz56^t#U}ddfF#6)-ccJKyVT-LIbYr*3mkj;U^%=h04iMNxw>FDm+VG`; zK4aEL7_ks0jc2vf90ZTuo^UP|3(h#12o6HGDBm_u}q+uY?HtQ1&`^#EK zQ}4fE(fWBtZ! zCh2ZD;+enFyuudKdtj6$U}QFp=KS7CBLu zn?1$5F&Or4HD*-Q2iuV>4D9SmGfHDQLJjG_3M$(%a1}{#To(4_X#B>meLg3^j-sy= z;vp3q!OWAXNnWF)t*m4F*i)Be+11wAVqIiq8T|v!Ln&&178{mLF&VxeNXL3y6Qtjg zD?T8DpJbdO*Ej$1Lhgv*(ulKf-I!fV>2u-y@Oy&2v4jb2-)w1G05eJSY(!cc8;MwbS`kHUspQ5rv`% zTI$6`CLbX6Q=dlS+GnG|I0Pv{_DeT>6nBWevYFcbswS%EZg8nw#tt9q~c3|nVL4iuhw^Xe!A^CwLTT?=R&lG))Z@@?Q-JIx7#t8XB< zw+hK#();X9Z12uBABw)}GxR&gKG<~)T0MTt=S(7WAF4GsS-%a@TCF_4{b1uY27adr-^5WC%S(ArjyIx*{f!BiWEG?+&Lox15` zhb`vUZ~dm9KlPHmb29K-Wze(ls(=3cdCtk?(tS06XxBSJa}d9xe*uNedo`R4Gc^TS z2gKm=r}hAiXD5JgHb!?Nt640Jv8~!JES(N0;|1lq$T1;kP{ursgfCfgaz}G*1+AuH z1+54m6q`z2&Pk>W@DeWatf-?S!C)(^hvNr-^N)Ydv{}{l`SSX7tHdPHQ!dK%M93d^ zcV_=52yJJPiNCG@mv5SczMyhF>Wfz9x1Uh#`lemivB!?EEzH!U;qo?^5vtm#M8`9WA&Hm|sC6se`F42ct!PiiFg^9vm)~3rtDKBb<^<+hNxl8)uOo6` znho01fhAmw#5q;r8oj|9s-BFi z%|t=UvjP-Pdn3diU7B(i`pGEubV#sdtA;|(m+3i4%F95X^aMr2VO1qaU=leaHdP-C zdjLU!@H*fl2rQx%!ayYR(?`$7RKZ@MLo|YVRCqi?Zl8eB7dquC8w|3e5Aj zy+Tz&F|c8aBqGMP{0K$TL7C0SO|cgr)8~kSf3)uI6B*^zu;b);80rsQbM-5+3Yf%{ z*XHZbdPubpqV0xV9#Zos#kBD7KaG9WXSnA`JIJxZ9&|bFMb4hi_iUU9z2vCiGQ{sY zP?~bI>MxV>%5d|B?$^Q*-tutquh2(feWYdBLuC%{xQohoKu(Cx^Sy_X_VlN11^$SE zGN+OKGqYI1fapW#qO*4lybi1^OODj5? z$2plV#HEm!T1C4&4$Ne>%IsS5UV%gtUS^Hmu&elc5Hri*pFoU?Bh8Y7n_q!x2mS~j z&Iy-&X%@}&1m6M)3l6Jza3yT&AZ7QAzXrjz#HH+O!NUS<>j)G)PMCoS*Kf2)g7VN_rOZguqRz2BiB z*n#mpS#&_txl90xm{wBb2Xw{q!3Ahd(D_8($H)qp%XX=^!_{d3P1b-IDd>lnXtSX&pVg;2*Ih22Mo-kxWE%xtBg?Ea%1?EsGg*A2^ zylIad{umh%=EzQ;MLvPA>Cs?Z7tmNlhLxjb*%_v;dochKt0ipQH#NN-M&Yqb2zG6b zOb6$56mH7CK+4xSv?OAQc+$B_&M3{Soi4A$JUC z8^zH8)GOd%%yEhB+S@P_VS-!dmu6=`;AgSYqSq+3jUyWXchbtQ4QJFLs9D`_|IjCU zj#h~zs_;2RA-Umdd!*1d&)KvL1Fyv@D84k;O61Tnzgl{~<3g6K$I;qz&Kf7()aul_ zS&gZAS@pY54W@+%9&BA&z)uw8!|SjN#mTD{*0Q=m;ZEkh+ORuRActNG_D?GUAJ z%p8#$t~_v=n3Y%9>-F9;1U_U<2hDuaS*bJUjhLLLFJt^Gr8of(~P{2TkDo%EyXGj z_2+nh8<{}pqB*0ZC4(O1Cu?sA1Ax_*rl9z#*T&Exr{AysB*JpLuFpvC?f~Tx0AlVm z+o7cqAQKwYI2i#Amy-Kix^XGnMRlXru!%9_#CzFd-E*>5Kt}pL;R$VCMz_? z^rGi049q6VDV-AHI+HtB7`-Iuvhj_tPw-8~FaE)5w6w$Ky4J?Oo#<0KYZfX@k(_V4gu0g#or>1t=ECuR`5$ssm#=dZc zG*NhM9WN5Gc7D|Wl-Gg^@l%-CO|>}u8%iAd%&{E@S9kCyqMMIxutl;MkOO(DRl=tD zA8?c_Ml1N5frv!6^!uS-!kPwtS2dC@xrApmGjP3RWX`A_9hnac%|e#Dx3B? z?FgV=hr$=m<3RB@6q0uH$GN?-& z3X3skPR^)+HN98`kQ-IkAS)!S%fiS+a$@7|&3SqOM$W}PXgplrJ%t^6JC=N);79>O z8FwY5P~nl|1swOdE5EYnxMFB@0bCD7@+`d>3Zak-t7&DzM{D8pOKMj&F|w%Jh3cE& zToA)4I@O?PGCk;XEwts8mQfQA+L&Ghtl#fO9MS42U@IK;%j(ub;)4Dk=85Ya8UO+0{@{R-VmWrA_mdb^%uBvMXx`wvQ6IUu6}8-U4Ah8Adkegsi^UOiVzEE_`|n?!s#rNIoO8)M zF((W|J;yIMGhP{PAaK7(vb}Sl5*>lexY2ke7l?q}k@3zGOJokJ*)u<}`_Yf>o%<%BJQN9d(rPiErb zL;y@nL?1)j4nnNbYH!7jnPOw~QRhJCIceSUJUbgsJSg0TvTmIv%USvNd8vFP$xHk? z2Qp&rm~(1qXJ;3%a&z1J2kscpAEs^k!*H;y3|)`wd|%;d5&^ghQZlBEn;`WRUI!Wk zeW>UQM&^%MMBi_C`y{e%FbZCGc%KRC^wssi8U6YX z0*x;}2H}@ONj9T|cn)6T4zQGFlv1`QhSIiA=0>c5G!b(oxq-7QB$wdlQozUI4}m_# z2;Y+z`;eu z0VKEyC>5Fp$Eq8-xd90Jy2NX0o>UV%)(}@^|vd44jE1TSS*VM}g3|#5#`<6{~A{&*}QUzPaisu6HePdwHdK z#;v9&DcsP*!x|hx9&ayed%KZO4sX-MCJk|iwR9d;%g4axqRn@G=` zMH0x483y3pQ@oriH*7P*&5fH?E-O16K#n%95YL+0+*0KI zY$}5xF$thQJ*4^jUx!-Un;*0UWr#aM2ImUVl_kgk;3JjO(n`Lq1IEA!AJ+Y`%h7C3 z0lE>PX6o6_Svcs~B9bWj7GCG#j4yu5w7mkKv#LR_FrJDE>!k(Jj&lnQtjBf2ezACR>@b7^H%k2Xw z$JC9zKv6n@@^(I+i4cnqItdLtO{Eze3}>ijzdd_~r(d9=FtW|4Kow(>_@ovV^M|lL zcYt-$5cC@1nD%Kx`xNewXXA)tT;3eO43?!Hba69|QT+p&Od=*4V=6H5YY?ZT5V1Lo z*nJ0}5>Gxm?at9dLFfr2H=V_!W!lQwh$y`^PFY9w2_LZ|z`q`lj0EB21CE^Hh<@n~ zq}f#FaLUA4Sc?~;K49b`Iai=tKl`Uy&*;4bkLM*&SJz5D?BG@-ViEo8-=B&QfuaUU zf}$yCIT%vTjYBg%n8sV2)NF_<%DG{fx;lFEl`XHxZ!&>KZ|rK+wvHDpOrpKcJHw8G zyf$bt#T!^Fn0}OvGxc&3ncf` zOGXzJ%rfUlU4~7Aj`lVuq!6P5Od^{I&TkOrQYf33aFJd*5QGGH4z16uAoWtZ zKF;E_GAF~ZZSew7)C| zCQ;62y%LD1GTSvz6yJw%{#_DT5V4UH^el2Ay5m$Pq|d{}(grgErAnzk6QC*vUxYn( z!@WZ-GBpNcKseaW*u(cYGPGVQ;_@j=RsLi?0O~oQ*_2g3egF`A@>9AbMSo z`ok6?`i`V4O9YI7XF=&y_bgXqXDp~A{#S?t)ELVq(SuPaKM%f=S(yjsOQNwpY?cyLc|@ljxZ(>Y1(J* zv9OpjEjSRJb+L*<#lp7(AjTkHfWmMPD`310R$Y`DckqVq;7)5u*+QTnP8baAa=Zj} z#Mk!wxr4Ad1JeeLJ6Nz&V!%USy=e=l#4um+qCTo9XuuTe3JiNzthq?ixN&U~buHv0 zv6ObRV{~>i|>r}TRmPBkdwe+r%@ye zGqfDhY4TYB4B@T5Z=Rerk^EnhzuXz(ICEyy*=dKi4I4e}>wsvf@J7Ee4Jtmp#4nsV z6q)(8MB;%14%a0mU7asHa@^haFv}z6_l#RA8E@Xau~LmVzfgJE*@ZWe0jqxh-J##N z-&WPstU-iWwsB+A*@C^dmTBTj?+5pi|KY<2TU*x4jjoMN zO;y#^%ki|p=jg8Lkwbmzi;9YT+FQNSeLjpkUbI40RrUGDkDKtcyXWogTQ+Ri@Eq_@ zJ<`sjva(n$x6+%KFoxw3X&n%q51=r8J~p`I7Zh}8{Bj{VS!={ppS^eQ-qo2r zc|YFxniplot`qBFqOm$TKg0K4L9AZY^XJR%zk2_qv@{$Slv^M})greUy`27^!bYPgs0C} zq*qVC|6)mcGSlZvGO{Pvf4(F=MakzY(o>Xtz9KzE$rmfqQ{jKHBt1pR7faGplzg!y zJw?gqOVU%6e7+*RdWw?Im!zjC`Fursijpr@q^BtPVo7?6k}sCz|94U1`_)GMthHj> Twqm9i9++ofqJL_-!@vI<%yH4W literal 0 HcmV?d00001 diff --git a/src/common/method_wrappers/results-perf-eval/emulated/exp2-context-device-rpcs.png b/src/common/method_wrappers/results-perf-eval/emulated/exp2-context-device-rpcs.png new file mode 100644 index 0000000000000000000000000000000000000000..188d784b68cbc1abb2ea90eaa305863b36cb838a GIT binary patch literal 233249 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 z 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 I*R99@3!d>BvH$=8 literal 0 HcmV?d00001 diff --git a/src/common/method_wrappers/results-perf-eval/emulated/exp1-svc-hdl-l2nm-emu.png b/src/common/method_wrappers/results-perf-eval/emulated/exp1-svc-hdl-l2nm-emu.png new file mode 100644 index 0000000000000000000000000000000000000000..3e58fcbb8d8a25c7bbbd2094a28dad419afbeb02 GIT binary patch literal 236416 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

    iOJq;4~!C&YNWOm%6Y71p7#427SXt1lM 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 KCVcbVkN*i5+AP2T literal 0 HcmV?d00001 diff --git a/src/common/method_wrappers/results-perf-eval/XR/dev-drv-xr.png b/src/common/method_wrappers/results-perf-eval/XR/dev-drv-xr.png new file mode 100644 index 0000000000000000000000000000000000000000..61f7d3e394676a05f240d032ce62124a70d8b8bb GIT binary patch literal 237646 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

    b!u+F%`z)N}rCh)}QrK1}v$e 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 literal 0 HcmV?d00001 diff --git a/src/common/method_wrappers/results-perf-eval/OpenConfig/srv-hlr-openconfig-l3nm.png b/src/common/method_wrappers/results-perf-eval/OpenConfig/srv-hlr-openconfig-l3nm.png new file mode 100644 index 0000000000000000000000000000000000000000..e4b2d83685dc983cbd2738eecf7064eba74c8e13 GIT binary patch literal 241795 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