From 7b2637c62d631a881213384a00ef1deb9f6afb05 Mon Sep 17 00:00:00 2001 From: Carlos Natalino <carlos.natalino@chalmers.se> Date: Wed, 2 Feb 2022 14:32:53 +0000 Subject: [PATCH] First version of the WebUI --- .gitlab-ci.yml | 1 + deploy_in_kubernetes.sh | 2 +- install_development_dependencies.sh | 2 +- manifests/webuiservice.yaml | 72 + open_webui.sh | 12 + run_tests_locally.sh | 3 + src/context/requirements.txt | 72 + src/device/client/DeviceClient.py | 2 +- src/start_webui_dev_mode.sh | 15 + src/webui/.gitlab-ci.yml | 89 + src/webui/Config.py | 23 + src/webui/Dockerfile | 46 + src/webui/__init__.py | 0 src/webui/genproto.sh | 43 + src/webui/proto/__init__.py | 0 src/webui/proto/context_pb2.py | 2535 +++++++++++++++++ src/webui/proto/device_pb2.py | 88 + src/webui/proto/monitoring_pb2.py | 617 ++++ src/webui/proto/service_pb2.py | 88 + src/webui/requirements.in | 11 + src/webui/service/__init__.py | 58 + src/webui/service/__main__.py | 27 + src/webui/service/context/__init__.py | 0 src/webui/service/context/routes.py | 9 + src/webui/service/device/__init__.py | 0 src/webui/service/device/forms.py | 41 + src/webui/service/device/routes.py | 121 + src/webui/service/main/__init__.py | 0 src/webui/service/main/forms.py | 11 + src/webui/service/main/routes.py | 37 + src/webui/service/service/__init__.py | 0 src/webui/service/service/routes.py | 39 + src/webui/service/static/partners.png | Bin 0 -> 76488 bytes src/webui/service/static/site.js | 4 + src/webui/service/templates/base.html | 119 + src/webui/service/templates/context/home.html | 79 + src/webui/service/templates/device/add.html | 93 + .../service/templates/device/detail.html | 73 + src/webui/service/templates/device/home.html | 93 + src/webui/service/templates/main/about.html | 9 + src/webui/service/templates/main/home.html | 37 + src/webui/service/templates/service/home.html | 95 + src/webui/tests/__init__.py | 0 src/webui/tests/test_unitary.py | 156 + src/webui/utils/__init__.py | 0 src/webui/utils/form_validators.py | 14 + 46 files changed, 4833 insertions(+), 3 deletions(-) create mode 100644 manifests/webuiservice.yaml create mode 100755 open_webui.sh create mode 100644 src/context/requirements.txt create mode 100755 src/start_webui_dev_mode.sh create mode 100644 src/webui/.gitlab-ci.yml create mode 100644 src/webui/Config.py create mode 100644 src/webui/Dockerfile create mode 100644 src/webui/__init__.py create mode 100755 src/webui/genproto.sh create mode 100644 src/webui/proto/__init__.py create mode 100644 src/webui/proto/context_pb2.py create mode 100644 src/webui/proto/device_pb2.py create mode 100644 src/webui/proto/monitoring_pb2.py create mode 100644 src/webui/proto/service_pb2.py create mode 100644 src/webui/requirements.in create mode 100644 src/webui/service/__init__.py create mode 100644 src/webui/service/__main__.py create mode 100644 src/webui/service/context/__init__.py create mode 100644 src/webui/service/context/routes.py create mode 100644 src/webui/service/device/__init__.py create mode 100644 src/webui/service/device/forms.py create mode 100644 src/webui/service/device/routes.py create mode 100644 src/webui/service/main/__init__.py create mode 100644 src/webui/service/main/forms.py create mode 100644 src/webui/service/main/routes.py create mode 100644 src/webui/service/service/__init__.py create mode 100644 src/webui/service/service/routes.py create mode 100644 src/webui/service/static/partners.png create mode 100644 src/webui/service/static/site.js create mode 100644 src/webui/service/templates/base.html create mode 100644 src/webui/service/templates/context/home.html create mode 100644 src/webui/service/templates/device/add.html create mode 100644 src/webui/service/templates/device/detail.html create mode 100644 src/webui/service/templates/device/home.html create mode 100644 src/webui/service/templates/main/about.html create mode 100644 src/webui/service/templates/main/home.html create mode 100644 src/webui/service/templates/service/home.html create mode 100644 src/webui/tests/__init__.py create mode 100644 src/webui/tests/test_unitary.py create mode 100644 src/webui/utils/__init__.py create mode 100644 src/webui/utils/form_validators.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9946d3290..3249b820f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,6 +22,7 @@ include: #- local: '/src/tester_integration/.gitlab-ci.yml' #- local: '/src/tester_functional/.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_centralizedattackdetector/.gitlab-ci.yml' # - local: '/src/l3_attackmitigator/.gitlab-ci.yml' diff --git a/deploy_in_kubernetes.sh b/deploy_in_kubernetes.sh index c4019c7d7..58e35d249 100755 --- a/deploy_in_kubernetes.sh +++ b/deploy_in_kubernetes.sh @@ -10,7 +10,7 @@ REGISTRY_IMAGE="" #REGISTRY_IMAGE="http://my-container-registry.local/" # Set the list of components you want to build images for, and deploy. -COMPONENTS="context device automation policy service compute monitoring dbscanserving opticalattackmitigator opticalcentralizedattackdetector" +COMPONENTS="context device automation policy service compute monitoring dbscanserving opticalattackmitigator opticalcentralizedattackdetector webui" # Set the tag you want to use for your images. IMAGE_TAG="tf-dev" diff --git a/install_development_dependencies.sh b/install_development_dependencies.sh index 4b66f9c92..701e844d7 100755 --- a/install_development_dependencies.sh +++ b/install_development_dependencies.sh @@ -7,7 +7,7 @@ pip install --upgrade pip setuptools wheel pip-tools pylint pytest pytest-benchm echo "" > requirements.in #TODO: include here your component -COMPONENTS="compute context device service monitoring opticalcentralizedattackdetector opticalattackmitigator dbscanserving" +COMPONENTS="compute context device service monitoring opticalcentralizedattackdetector opticalattackmitigator dbscanserving webui" # compiling dependencies from all components for component in $COMPONENTS diff --git a/manifests/webuiservice.yaml b/manifests/webuiservice.yaml new file mode 100644 index 000000000..901328d27 --- /dev/null +++ b/manifests/webuiservice.yaml @@ -0,0 +1,72 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: webuiservice +spec: + selector: + matchLabels: + app: webuiservice + template: + metadata: + labels: + app: webuiservice + spec: + terminationGracePeriodSeconds: 5 + containers: + - name: server + image: registry.gitlab.com/teraflow-h2020/controller/webui:latest + imagePullPolicy: Always + ports: + - containerPort: 8004 # TODO: define the real port + env: + - name: LOG_LEVEL + value: "DEBUG" + readinessProbe: + httpGet: + path: /healthz/ready + port: 8004 + initialDelaySeconds: 5 + timeoutSeconds: 1 + livenessProbe: + httpGet: + path: /healthz/live + port: 8004 + initialDelaySeconds: 5 + timeoutSeconds: 1 + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: 700m + memory: 1024Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: webuiservice +spec: + type: ClusterIP + selector: + app: webuiservice + ports: + - name: http + port: 8004 + targetPort: 8004 +--- +# 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 +--- diff --git a/open_webui.sh b/open_webui.sh new file mode 100755 index 000000000..83d25d9c7 --- /dev/null +++ b/open_webui.sh @@ -0,0 +1,12 @@ +# this script opens the webui + +WEBUI_PROTO=`kubectl get service/webuiservice -n tf-dev -o jsonpath='{.spec.ports[0].name}'` +WEBUI_IP=`kubectl get service/webuiservice -n tf-dev -o jsonpath='{.spec.clusterIP}'` +WEBUI_PORT=`kubectl get service/webuiservice -n tf-dev -o jsonpath='{.spec.ports[0].port}'` +URL=${WEBUI_PROTO}://${WEBUI_IP}:${WEBUI_PORT} + +echo Opening web UI on URL ${URL} + +# curl -kL ${URL} + +python3 -m webbrowser ${URL} \ No newline at end of file diff --git a/run_tests_locally.sh b/run_tests_locally.sh index eb421c608..11add2a82 100755 --- a/run_tests_locally.sh +++ b/run_tests_locally.sh @@ -59,3 +59,6 @@ coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ compute/tests/test_unitary.py + +coverage run --rcfile=$RCFILE --append -m pytest --log-level=INFO --verbose \ + webui/tests/test_unitary.py diff --git a/src/context/requirements.txt b/src/context/requirements.txt new file mode 100644 index 000000000..1f0e17131 --- /dev/null +++ b/src/context/requirements.txt @@ -0,0 +1,72 @@ +# +# This file is autogenerated by pip-compile with python 3.9 +# To update, run: +# +# pip-compile src/context/requirements.in +# +aniso8601==9.0.1 + # via flask-restful +attrs==21.2.0 + # via pytest +certifi==2021.5.30 + # via requests +charset-normalizer==2.0.6 + # via requests +click==8.0.1 + # via flask +flask==2.0.1 + # via flask-restful +flask-restful==0.3.9 + # via -r src/context/requirements.in +grpcio==1.41.0 + # via + # -r src/context/requirements.in + # grpcio-health-checking +grpcio-health-checking==1.41.0 + # via -r src/context/requirements.in +idna==3.2 + # via requests +iniconfig==1.1.1 + # via pytest +itsdangerous==2.0.1 + # via flask +jinja2==3.0.1 + # via flask +markupsafe==2.0.1 + # via jinja2 +packaging==21.0 + # via pytest +pluggy==1.0.0 + # via pytest +prometheus-client==0.11.0 + # via -r src/context/requirements.in +protobuf==3.18.0 + # via grpcio-health-checking +py==1.10.0 + # via pytest +py-cpuinfo==8.0.0 + # via pytest-benchmark +pyparsing==2.4.7 + # via packaging +pytest==6.2.5 + # via + # -r src/context/requirements.in + # pytest-benchmark +pytest-benchmark==3.4.1 + # via -r src/context/requirements.in +pytz==2021.1 + # via flask-restful +redis==3.5.3 + # via -r src/context/requirements.in +requests==2.26.0 + # via -r src/context/requirements.in +six==1.16.0 + # via + # flask-restful + # grpcio +toml==0.10.2 + # via pytest +urllib3==1.26.7 + # via requests +werkzeug==2.0.1 + # via flask diff --git a/src/device/client/DeviceClient.py b/src/device/client/DeviceClient.py index 7c5fa0ca2..17f0ac715 100644 --- a/src/device/client/DeviceClient.py +++ b/src/device/client/DeviceClient.py @@ -22,7 +22,7 @@ class DeviceClient: self.stub = DeviceServiceStub(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 diff --git a/src/start_webui_dev_mode.sh b/src/start_webui_dev_mode.sh new file mode 100755 index 000000000..32cf9c7f1 --- /dev/null +++ b/src/start_webui_dev_mode.sh @@ -0,0 +1,15 @@ +# for development purposes only + +export CONTEXT_SERVICE_ADDRESS=`kubectl get service/contextservice -n tf-dev -o jsonpath='{.spec.clusterIP}'` + +echo $CONTEXT_SERVICE_ADDRESS + +export DEVICE_SERVICE_ADDRESS=`kubectl get service/deviceservice -n tf-dev -o jsonpath='{.spec.clusterIP}'` + +echo $DEVICE_SERVICE_ADDRESS + +export HOST="127.0.0.1" + +export FLASK_ENV="development" + +python -m webui.service diff --git a/src/webui/.gitlab-ci.yml b/src/webui/.gitlab-ci.yml new file mode 100644 index 000000000..d803c72de --- /dev/null +++ b/src/webui/.gitlab-ci.yml @@ -0,0 +1,89 @@ +# build, tag and push the Docker image to the gitlab registry +build webui: + variables: + IMAGE_NAME: 'webui' # 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 + - src/$IMAGE_NAME/tests/Dockerfile + - manifests/$IMAGE_NAME.yaml + - .gitlab-ci.yml + +# apply unit test to the webui component +unit test webui: + variables: + IMAGE_NAME: 'webui' # name of the microservice + IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) + stage: unit_test + needs: + - build webui + 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 8004:8004 -v "$PWD/src/$IMAGE_NAME/tests:/opt/results" --network=teraflowbridge --rm $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=DEBUG --verbose ${IMAGE_NAME}/tests/test_unitary.py --junitxml=/opt/results/${IMAGE_NAME}_report.xml; coverage xml -o /opt/results/${IMAGE_NAME}_coverage.xml; ls -la /opt/results; 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.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 webui service in Kubernetes Cluster +deploy webui: + variables: + IMAGE_NAME: 'webui' # name of the microservice + IMAGE_TAG: 'latest' # tag of the container image (production, development, etc) + stage: deploy + needs: + - unit test webui + # - integ_test execute + script: + - 'sed -i "s/$IMAGE_NAME:.*/$IMAGE_NAME:$IMAGE_TAG/" manifests/$IMAGE_NAME.yaml' + - kubectl version + - kubectl get all + - kubectl apply -f "manifests/$IMAGE_NAME.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 \ No newline at end of file diff --git a/src/webui/Config.py b/src/webui/Config.py new file mode 100644 index 000000000..e4ea65f4a --- /dev/null +++ b/src/webui/Config.py @@ -0,0 +1,23 @@ +import os +import logging + +# General settings +LOG_LEVEL = logging.DEBUG + +# gRPC settings +WEBUI_SERVICE_PORT = 8004 + +# Prometheus settings +METRICS_PORT = 9192 + +SECRET_KEY = '>s&}24@{]]#k3&^5$f3#?6?h3{W@[}/7z}2pa]>{3&5%RP<)[(' + +HOST = '0.0.0.0' # accepts connections coming from any ADDRESS + +DEBUG=False + +CONTEXT_SERVICE_ADDRESS = os.environ.get('CONTEXTSERVICE_SERVICE_HOST', 'contextservice') +CONTEXT_SERVICE_PORT = 1010 + +DEVICE_SERVICE_ADDRESS = os.environ.get('DEVICESERVICE_SERVICE_HOST', 'deviceservice') +DEVICE_SERVICE_PORT = 2020 diff --git a/src/webui/Dockerfile b/src/webui/Dockerfile new file mode 100644 index 000000000..24c5c5296 --- /dev/null +++ b/src/webui/Dockerfile @@ -0,0 +1,46 @@ +FROM python:3-slim + +# Ref: https://pythonspeed.com/articles/activate-virtualenv-dockerfile/ + +# 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 +ENV PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION='python' + +# Download the gRPC health probe -- not needed here... health will be asserted using HTTP +# 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 + +# creating a user for security reasons +RUN groupadd -r webui && useradd --no-log-init -r -m -g webui webui +USER webui + +# set working directory +RUN mkdir -p /home/webui/teraflow +WORKDIR /home/webui/teraflow + +# Get Python packages per module +ENV VIRTUAL_ENV=/home/webui/venv +RUN python3 -m venv ${VIRTUAL_ENV} +ENV PATH="${VIRTUAL_ENV}/bin:${PATH}" +COPY --chown=webui:webui webui/requirements.in /home/webui/teraflow/webui/requirements.in +RUN pip install --upgrade "pip<22" setuptools wheel pip-tools && pip-compile --output-file=webui/requirements.txt webui/requirements.in +RUN pip install -r webui/requirements.txt + +# Add files into working directory +COPY --chown=webui:webui common/. common +COPY --chown=webui:webui context/__init__.py context/__init__.py +COPY --chown=webui:webui context/proto/. context/proto +COPY --chown=webui:webui context/client/. context/client +COPY --chown=webui:webui device/__init__.py device/__init__.py +COPY --chown=webui:webui device/proto/. device/proto +COPY --chown=webui:webui device/client/. device/client +COPY --chown=webui:webui webui/. webui + +# Start webui service +ENTRYPOINT ["python", "-m", "webui.service"] diff --git a/src/webui/__init__.py b/src/webui/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/webui/genproto.sh b/src/webui/genproto.sh new file mode 100755 index 000000000..1cd87af70 --- /dev/null +++ b/src/webui/genproto.sh @@ -0,0 +1,43 @@ +#!/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 + +# building protos of services used +# python -m grpc_tools.protoc -I../../proto --python_out=proto --grpc_python_out=proto compute.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 device.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 monitoring.proto + +# rm proto/compute_pb2_grpc.py +rm proto/context_pb2_grpc.py +rm proto/device_pb2_grpc.py +rm proto/service_pb2_grpc.py +rm proto/monitoring_pb2_grpc.py + +# sed -i -E 's/(import\ .*)_pb2/from . \1_pb2/g' proto/compute_pb2.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/device_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/monitoring_pb2.py diff --git a/src/webui/proto/__init__.py b/src/webui/proto/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/webui/proto/context_pb2.py b/src/webui/proto/context_pb2.py new file mode 100644 index 000000000..8b4848bc3 --- /dev/null +++ b/src/webui/proto/context_pb2.py @@ -0,0 +1,2535 @@ +# -*- 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() + + + + +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\"\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\"\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\"K\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\"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\"6\n\x0c\x43onnectionId\x12&\n\x0f\x63onnection_uuid\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\x8d\x01\n\nConnection\x12,\n\rconnection_id\x18\x01 \x01(\x0b\x32\x15.context.ConnectionId\x12.\n\x12related_service_id\x18\x02 \x01(\x0b\x32\x12.context.ServiceId\x12!\n\x04path\x18\x03 \x03(\x0b\x32\x13.context.EndPointId\"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\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\xa5\r\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\x62\x06proto3' +) + +_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=3468, + serialized_end=3574, +) +_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=3577, + serialized_end=3774, +) +_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=3777, + serialized_end=3920, +) +_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=3923, + serialized_end=4052, +) +_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=4055, + serialized_end=4191, +) +_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=4193, + serialized_end=4286, +) +_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=26, + serialized_end=33, +) + + +_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=35, + serialized_end=55, +) + + +_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=57, + serialized_end=127, +) + + +_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=129, + serialized_end=177, +) + + +_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=180, + serialized_end=362, +) + + +_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=364, + serialized_end=420, +) + + +_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=422, + serialized_end=471, +) + + +_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=473, + serialized_end=558, +) + + +_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=560, + serialized_end=650, +) + + +_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=652, + serialized_end=778, +) + + +_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=780, + serialized_end=839, +) + + +_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=841, + serialized_end=894, +) + + +_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=896, + serialized_end=984, +) + + +_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=986, + serialized_end=1032, +) + + +_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=1035, + serialized_end=1317, +) + + +_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=1319, + serialized_end=1376, +) + + +_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=1378, + serialized_end=1431, +) + + +_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=1433, + serialized_end=1479, +) + + +_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=1481, + serialized_end=1563, +) + + +_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=1565, + serialized_end=1607, +) + + +_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=1609, + serialized_end=1697, +) + + +_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=1699, + serialized_end=1746, +) + + +_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=1748, + serialized_end=1788, +) + + +_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=1790, + serialized_end=1866, +) + + +_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=1868, + serialized_end=1956, +) + + +_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=1959, + serialized_end=2253, +) + + +_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=2255, + serialized_end=2322, +) + + +_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=2324, + serialized_end=2382, +) + + +_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=2384, + serialized_end=2440, +) + + +_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=2442, + serialized_end=2491, +) + + +_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=2493, + serialized_end=2578, +) + + +_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=2581, + serialized_end=2711, +) + + +_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), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=2713, + serialized_end=2788, +) + + +_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=2790, + serialized_end=2891, +) + + +_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=2893, + serialized_end=2956, +) + + +_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=2958, + serialized_end=3012, +) + + +_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='related_service_id', full_name='context.Connection.related_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', full_name='context.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=3015, + serialized_end=3156, +) + + +_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=3158, + serialized_end=3223, +) + + +_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=3225, + serialized_end=3283, +) + + +_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=3285, + serialized_end=3379, +) + + +_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=3381, + serialized_end=3466, +) + +_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 +_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 +_CONFIGRULE.fields_by_name['action'].enum_type = _CONFIGACTIONENUM +_CONNECTIONID.fields_by_name['connection_uuid'].message_type = _UUID +_CONNECTION.fields_by_name['connection_id'].message_type = _CONNECTIONID +_CONNECTION.fields_by_name['related_service_id'].message_type = _SERVICEID +_CONNECTION.fields_by_name['path'].message_type = _ENDPOINTID +_CONNECTIONIDLIST.fields_by_name['connection_ids'].message_type = _CONNECTIONID +_CONNECTIONLIST.fields_by_name['connections'].message_type = _CONNECTION +_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['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['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['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) + +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) + +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) + +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=4289, + serialized_end=5990, + 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, + ), +]) +_sym_db.RegisterServiceDescriptor(_CONTEXTSERVICE) + +DESCRIPTOR.services_by_name['ContextService'] = _CONTEXTSERVICE + +# @@protoc_insertion_point(module_scope) diff --git a/src/webui/proto/device_pb2.py b/src/webui/proto/device_pb2.py new file mode 100644 index 000000000..e351738e6 --- /dev/null +++ b/src/webui/proto/device_pb2.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: device.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='device.proto', + package='device', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x0c\x64\x65vice.proto\x12\x06\x64\x65vice\x1a\rcontext.proto2\xf0\x01\n\rDeviceService\x12\x31\n\tAddDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x37\n\x0f\x43onfigureDevice\x12\x0f.context.Device\x1a\x11.context.DeviceId\"\x00\x12\x33\n\x0c\x44\x65leteDevice\x12\x11.context.DeviceId\x1a\x0e.context.Empty\"\x00\x12>\n\x10GetInitialConfig\x12\x11.context.DeviceId\x1a\x15.context.DeviceConfig\"\x00\x62\x06proto3' + , + dependencies=[context__pb2.DESCRIPTOR,]) + + + +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + +_DEVICESERVICE = _descriptor.ServiceDescriptor( + name='DeviceService', + full_name='device.DeviceService', + file=DESCRIPTOR, + index=0, + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_start=40, + serialized_end=280, + methods=[ + _descriptor.MethodDescriptor( + name='AddDevice', + full_name='device.DeviceService.AddDevice', + index=0, + containing_service=None, + input_type=context__pb2._DEVICE, + output_type=context__pb2._DEVICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='ConfigureDevice', + full_name='device.DeviceService.ConfigureDevice', + index=1, + containing_service=None, + input_type=context__pb2._DEVICE, + output_type=context__pb2._DEVICEID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='DeleteDevice', + full_name='device.DeviceService.DeleteDevice', + index=2, + containing_service=None, + input_type=context__pb2._DEVICEID, + output_type=context__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetInitialConfig', + full_name='device.DeviceService.GetInitialConfig', + index=3, + containing_service=None, + input_type=context__pb2._DEVICEID, + output_type=context__pb2._DEVICECONFIG, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), +]) +_sym_db.RegisterServiceDescriptor(_DEVICESERVICE) + +DESCRIPTOR.services_by_name['DeviceService'] = _DEVICESERVICE + +# @@protoc_insertion_point(module_scope) diff --git a/src/webui/proto/monitoring_pb2.py b/src/webui/proto/monitoring_pb2.py new file mode 100644 index 000000000..7368609d2 --- /dev/null +++ b/src/webui/proto/monitoring_pb2.py @@ -0,0 +1,617 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: monitoring.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='monitoring.proto', + package='monitoring', + syntax='proto3', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n\x10monitoring.proto\x12\nmonitoring\x1a\rcontext.proto\"\x84\x01\n\x10\x43reateKpiRequest\x12\x16\n\x0ekpiDescription\x18\x01 \x01(\t\x12$\n\tdevice_id\x18\x02 \x01(\x0b\x32\x11.context.DeviceId\x12\x32\n\x0fkpi_sample_type\x18\x03 \x01(\x0e\x32\x19.monitoring.KpiSampleType\"h\n\x11MonitorKpiRequest\x12!\n\x06kpi_id\x18\x01 \x01(\x0b\x32\x11.monitoring.KpiId\x12\x18\n\x10\x63onnexion_time_s\x18\x02 \x01(\r\x12\x16\n\x0esample_rate_ms\x18\x03 \x01(\r\"i\n\x17MonitorDeviceKpiRequest\x12\x1c\n\x03kpi\x18\x01 \x01(\x0b\x32\x0f.monitoring.Kpi\x12\x18\n\x10\x63onnexion_time_s\x18\x02 \x01(\r\x12\x16\n\x0esample_rate_ms\x18\x03 \x01(\r\"s\n\x11IncludeKpiRequest\x12!\n\x06kpi_id\x18\x01 \x01(\x0b\x32\x11.monitoring.KpiId\x12\x12\n\ntime_stamp\x18\x02 \x01(\t\x12\'\n\tkpi_value\x18\x03 \x01(\x0b\x32\x14.monitoring.KpiValue\"&\n\x05KpiId\x12\x1d\n\x06kpi_id\x18\x01 \x01(\x0b\x32\r.context.Uuid\"\xd6\x01\n\x03Kpi\x12!\n\x06kpi_id\x18\x01 \x01(\x0b\x32\x11.monitoring.KpiId\x12\x11\n\ttimestamp\x18\x02 \x01(\t\x12\x16\n\x0ekpiDescription\x18\x03 \x01(\t\x12\'\n\tkpi_value\x18\x04 \x01(\x0b\x32\x14.monitoring.KpiValue\x12\x32\n\x0fkpi_sample_type\x18\x05 \x01(\x0e\x32\x19.monitoring.KpiSampleType\x12$\n\tdevice_id\x18\x06 \x01(\x0b\x32\x11.context.DeviceId\"a\n\x08KpiValue\x12\x10\n\x06intVal\x18\x01 \x01(\rH\x00\x12\x12\n\x08\x66loatVal\x18\x02 \x01(\x02H\x00\x12\x13\n\tstringVal\x18\x03 \x01(\tH\x00\x12\x11\n\x07\x62oolVal\x18\x04 \x01(\x08H\x00\x42\x07\n\x05value\"+\n\x07KpiList\x12 \n\x07kpiList\x18\x01 \x03(\x0b\x32\x0f.monitoring.Kpi*x\n\rKpiSampleType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x17\n\x13PACKETS_TRANSMITTED\x10\x65\x12\x14\n\x10PACKETS_RECEIVED\x10\x66\x12\x16\n\x11\x42YTES_TRANSMITTED\x10\xc9\x01\x12\x13\n\x0e\x42YTES_RECEIVED\x10\xca\x01\x32\x8b\x03\n\x11MonitoringService\x12>\n\tCreateKpi\x12\x1c.monitoring.CreateKpiRequest\x1a\x11.monitoring.KpiId\"\x00\x12=\n\nIncludeKpi\x12\x1d.monitoring.IncludeKpiRequest\x1a\x0e.context.Empty\"\x00\x12=\n\nMonitorKpi\x12\x1d.monitoring.MonitorKpiRequest\x1a\x0e.context.Empty\"\x00\x12I\n\x10MonitorDeviceKpi\x12#.monitoring.MonitorDeviceKpiRequest\x1a\x0e.context.Empty\"\x00\x12\x36\n\x0cGetStreamKpi\x12\x11.monitoring.KpiId\x1a\x0f.monitoring.Kpi\"\x00\x30\x01\x12\x35\n\rGetInstantKpi\x12\x11.monitoring.KpiId\x1a\x0f.monitoring.Kpi\"\x00\x62\x06proto3' + , + dependencies=[context__pb2.DESCRIPTOR,]) + +_KPISAMPLETYPE = _descriptor.EnumDescriptor( + name='KpiSampleType', + full_name='monitoring.KpiSampleType', + 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='PACKETS_TRANSMITTED', index=1, number=101, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PACKETS_RECEIVED', index=2, number=102, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='BYTES_TRANSMITTED', index=3, number=201, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='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=913, + serialized_end=1033, +) +_sym_db.RegisterEnumDescriptor(_KPISAMPLETYPE) + +KpiSampleType = enum_type_wrapper.EnumTypeWrapper(_KPISAMPLETYPE) +UNKNOWN = 0 +PACKETS_TRANSMITTED = 101 +PACKETS_RECEIVED = 102 +BYTES_TRANSMITTED = 201 +BYTES_RECEIVED = 202 + + + +_CREATEKPIREQUEST = _descriptor.Descriptor( + name='CreateKpiRequest', + full_name='monitoring.CreateKpiRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='kpiDescription', full_name='monitoring.CreateKpiRequest.kpiDescription', 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='device_id', full_name='monitoring.CreateKpiRequest.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='kpi_sample_type', full_name='monitoring.CreateKpiRequest.kpi_sample_type', index=2, + number=3, 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=48, + serialized_end=180, +) + + +_MONITORKPIREQUEST = _descriptor.Descriptor( + name='MonitorKpiRequest', + full_name='monitoring.MonitorKpiRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='kpi_id', full_name='monitoring.MonitorKpiRequest.kpi_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='connexion_time_s', full_name='monitoring.MonitorKpiRequest.connexion_time_s', index=1, + number=2, 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), + _descriptor.FieldDescriptor( + name='sample_rate_ms', full_name='monitoring.MonitorKpiRequest.sample_rate_ms', 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=182, + serialized_end=286, +) + + +_MONITORDEVICEKPIREQUEST = _descriptor.Descriptor( + name='MonitorDeviceKpiRequest', + full_name='monitoring.MonitorDeviceKpiRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='kpi', full_name='monitoring.MonitorDeviceKpiRequest.kpi', 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='connexion_time_s', full_name='monitoring.MonitorDeviceKpiRequest.connexion_time_s', index=1, + number=2, 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), + _descriptor.FieldDescriptor( + name='sample_rate_ms', full_name='monitoring.MonitorDeviceKpiRequest.sample_rate_ms', 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=288, + serialized_end=393, +) + + +_INCLUDEKPIREQUEST = _descriptor.Descriptor( + name='IncludeKpiRequest', + full_name='monitoring.IncludeKpiRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='kpi_id', full_name='monitoring.IncludeKpiRequest.kpi_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='time_stamp', full_name='monitoring.IncludeKpiRequest.time_stamp', 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_value', full_name='monitoring.IncludeKpiRequest.kpi_value', 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=395, + serialized_end=510, +) + + +_KPIID = _descriptor.Descriptor( + name='KpiId', + full_name='monitoring.KpiId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='kpi_id', full_name='monitoring.KpiId.kpi_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=512, + serialized_end=550, +) + + +_KPI = _descriptor.Descriptor( + name='Kpi', + full_name='monitoring.Kpi', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='kpi_id', full_name='monitoring.Kpi.kpi_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='timestamp', full_name='monitoring.Kpi.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='kpiDescription', full_name='monitoring.Kpi.kpiDescription', 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='kpi_value', full_name='monitoring.Kpi.kpi_value', 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), + _descriptor.FieldDescriptor( + name='kpi_sample_type', full_name='monitoring.Kpi.kpi_sample_type', index=4, + number=5, 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_id', full_name='monitoring.Kpi.device_id', 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=553, + serialized_end=767, +) + + +_KPIVALUE = _descriptor.Descriptor( + name='KpiValue', + full_name='monitoring.KpiValue', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='intVal', full_name='monitoring.KpiValue.intVal', index=0, + number=1, 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), + _descriptor.FieldDescriptor( + name='floatVal', full_name='monitoring.KpiValue.floatVal', 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='stringVal', full_name='monitoring.KpiValue.stringVal', 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='boolVal', full_name='monitoring.KpiValue.boolVal', index=3, + number=4, 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=[ + _descriptor.OneofDescriptor( + name='value', full_name='monitoring.KpiValue.value', + index=0, containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=769, + serialized_end=866, +) + + +_KPILIST = _descriptor.Descriptor( + name='KpiList', + full_name='monitoring.KpiList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='kpiList', full_name='monitoring.KpiList.kpiList', 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=868, + serialized_end=911, +) + +_CREATEKPIREQUEST.fields_by_name['device_id'].message_type = context__pb2._DEVICEID +_CREATEKPIREQUEST.fields_by_name['kpi_sample_type'].enum_type = _KPISAMPLETYPE +_MONITORKPIREQUEST.fields_by_name['kpi_id'].message_type = _KPIID +_MONITORDEVICEKPIREQUEST.fields_by_name['kpi'].message_type = _KPI +_INCLUDEKPIREQUEST.fields_by_name['kpi_id'].message_type = _KPIID +_INCLUDEKPIREQUEST.fields_by_name['kpi_value'].message_type = _KPIVALUE +_KPIID.fields_by_name['kpi_id'].message_type = context__pb2._UUID +_KPI.fields_by_name['kpi_id'].message_type = _KPIID +_KPI.fields_by_name['kpi_value'].message_type = _KPIVALUE +_KPI.fields_by_name['kpi_sample_type'].enum_type = _KPISAMPLETYPE +_KPI.fields_by_name['device_id'].message_type = context__pb2._DEVICEID +_KPIVALUE.oneofs_by_name['value'].fields.append( + _KPIVALUE.fields_by_name['intVal']) +_KPIVALUE.fields_by_name['intVal'].containing_oneof = _KPIVALUE.oneofs_by_name['value'] +_KPIVALUE.oneofs_by_name['value'].fields.append( + _KPIVALUE.fields_by_name['floatVal']) +_KPIVALUE.fields_by_name['floatVal'].containing_oneof = _KPIVALUE.oneofs_by_name['value'] +_KPIVALUE.oneofs_by_name['value'].fields.append( + _KPIVALUE.fields_by_name['stringVal']) +_KPIVALUE.fields_by_name['stringVal'].containing_oneof = _KPIVALUE.oneofs_by_name['value'] +_KPIVALUE.oneofs_by_name['value'].fields.append( + _KPIVALUE.fields_by_name['boolVal']) +_KPIVALUE.fields_by_name['boolVal'].containing_oneof = _KPIVALUE.oneofs_by_name['value'] +_KPILIST.fields_by_name['kpiList'].message_type = _KPI +DESCRIPTOR.message_types_by_name['CreateKpiRequest'] = _CREATEKPIREQUEST +DESCRIPTOR.message_types_by_name['MonitorKpiRequest'] = _MONITORKPIREQUEST +DESCRIPTOR.message_types_by_name['MonitorDeviceKpiRequest'] = _MONITORDEVICEKPIREQUEST +DESCRIPTOR.message_types_by_name['IncludeKpiRequest'] = _INCLUDEKPIREQUEST +DESCRIPTOR.message_types_by_name['KpiId'] = _KPIID +DESCRIPTOR.message_types_by_name['Kpi'] = _KPI +DESCRIPTOR.message_types_by_name['KpiValue'] = _KPIVALUE +DESCRIPTOR.message_types_by_name['KpiList'] = _KPILIST +DESCRIPTOR.enum_types_by_name['KpiSampleType'] = _KPISAMPLETYPE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +CreateKpiRequest = _reflection.GeneratedProtocolMessageType('CreateKpiRequest', (_message.Message,), { + 'DESCRIPTOR' : _CREATEKPIREQUEST, + '__module__' : 'monitoring_pb2' + # @@protoc_insertion_point(class_scope:monitoring.CreateKpiRequest) + }) +_sym_db.RegisterMessage(CreateKpiRequest) + +MonitorKpiRequest = _reflection.GeneratedProtocolMessageType('MonitorKpiRequest', (_message.Message,), { + 'DESCRIPTOR' : _MONITORKPIREQUEST, + '__module__' : 'monitoring_pb2' + # @@protoc_insertion_point(class_scope:monitoring.MonitorKpiRequest) + }) +_sym_db.RegisterMessage(MonitorKpiRequest) + +MonitorDeviceKpiRequest = _reflection.GeneratedProtocolMessageType('MonitorDeviceKpiRequest', (_message.Message,), { + 'DESCRIPTOR' : _MONITORDEVICEKPIREQUEST, + '__module__' : 'monitoring_pb2' + # @@protoc_insertion_point(class_scope:monitoring.MonitorDeviceKpiRequest) + }) +_sym_db.RegisterMessage(MonitorDeviceKpiRequest) + +IncludeKpiRequest = _reflection.GeneratedProtocolMessageType('IncludeKpiRequest', (_message.Message,), { + 'DESCRIPTOR' : _INCLUDEKPIREQUEST, + '__module__' : 'monitoring_pb2' + # @@protoc_insertion_point(class_scope:monitoring.IncludeKpiRequest) + }) +_sym_db.RegisterMessage(IncludeKpiRequest) + +KpiId = _reflection.GeneratedProtocolMessageType('KpiId', (_message.Message,), { + 'DESCRIPTOR' : _KPIID, + '__module__' : 'monitoring_pb2' + # @@protoc_insertion_point(class_scope:monitoring.KpiId) + }) +_sym_db.RegisterMessage(KpiId) + +Kpi = _reflection.GeneratedProtocolMessageType('Kpi', (_message.Message,), { + 'DESCRIPTOR' : _KPI, + '__module__' : 'monitoring_pb2' + # @@protoc_insertion_point(class_scope:monitoring.Kpi) + }) +_sym_db.RegisterMessage(Kpi) + +KpiValue = _reflection.GeneratedProtocolMessageType('KpiValue', (_message.Message,), { + 'DESCRIPTOR' : _KPIVALUE, + '__module__' : 'monitoring_pb2' + # @@protoc_insertion_point(class_scope:monitoring.KpiValue) + }) +_sym_db.RegisterMessage(KpiValue) + +KpiList = _reflection.GeneratedProtocolMessageType('KpiList', (_message.Message,), { + 'DESCRIPTOR' : _KPILIST, + '__module__' : 'monitoring_pb2' + # @@protoc_insertion_point(class_scope:monitoring.KpiList) + }) +_sym_db.RegisterMessage(KpiList) + + + +_MONITORINGSERVICE = _descriptor.ServiceDescriptor( + name='MonitoringService', + full_name='monitoring.MonitoringService', + file=DESCRIPTOR, + index=0, + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_start=1036, + serialized_end=1431, + methods=[ + _descriptor.MethodDescriptor( + name='CreateKpi', + full_name='monitoring.MonitoringService.CreateKpi', + index=0, + containing_service=None, + input_type=_CREATEKPIREQUEST, + output_type=_KPIID, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='IncludeKpi', + full_name='monitoring.MonitoringService.IncludeKpi', + index=1, + containing_service=None, + input_type=_INCLUDEKPIREQUEST, + output_type=context__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='MonitorKpi', + full_name='monitoring.MonitoringService.MonitorKpi', + index=2, + containing_service=None, + input_type=_MONITORKPIREQUEST, + output_type=context__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='MonitorDeviceKpi', + full_name='monitoring.MonitoringService.MonitorDeviceKpi', + index=3, + containing_service=None, + input_type=_MONITORDEVICEKPIREQUEST, + output_type=context__pb2._EMPTY, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetStreamKpi', + full_name='monitoring.MonitoringService.GetStreamKpi', + index=4, + containing_service=None, + input_type=_KPIID, + output_type=_KPI, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.MethodDescriptor( + name='GetInstantKpi', + full_name='monitoring.MonitoringService.GetInstantKpi', + index=5, + containing_service=None, + input_type=_KPIID, + output_type=_KPI, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), +]) +_sym_db.RegisterServiceDescriptor(_MONITORINGSERVICE) + +DESCRIPTOR.services_by_name['MonitoringService'] = _MONITORINGSERVICE + +# @@protoc_insertion_point(module_scope) diff --git a/src/webui/proto/service_pb2.py b/src/webui/proto/service_pb2.py new file mode 100644 index 000000000..7a006915b --- /dev/null +++ b/src/webui/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/webui/requirements.in b/src/webui/requirements.in new file mode 100644 index 000000000..8d30ba299 --- /dev/null +++ b/src/webui/requirements.in @@ -0,0 +1,11 @@ +flask +flask-wtf +flask-healthz +flask-unittest +grpcio +grpcio-health-checking +prometheus-client +pytest +pytest-benchmark +lorem-text +coverage diff --git a/src/webui/service/__init__.py b/src/webui/service/__init__.py new file mode 100644 index 000000000..28197a9f5 --- /dev/null +++ b/src/webui/service/__init__.py @@ -0,0 +1,58 @@ +import os +from flask import Flask, session +from flask_healthz import healthz, HealthError + +from webui.proto.context_pb2 import Empty +from device.client.DeviceClient import DeviceClient +from context.client.ContextClient import ContextClient +from webui.Config import (CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT, + DEVICE_SERVICE_ADDRESS, DEVICE_SERVICE_PORT) + + +def get_working_context() -> str: + if 'context_uuid' in session: + return session['context_uuid'] + else: + return 'Not selected' + + +def liveness(): + pass + + +def readiness(): + try: # this component is ready when it is able to connect with the other components it depends on + context_client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT) + context_client.connect() + context_client.close() + device_client: DeviceClient = DeviceClient(DEVICE_SERVICE_ADDRESS, DEVICE_SERVICE_PORT) + device_client.connect() + device_client.close() + except Exception as e: + raise HealthError('Can\'t connect with the service: ' + e.details()) + + +def create_app(use_config=None): + app = Flask(__name__) + if use_config: + app.config.from_mapping(**use_config) + + app.config.update(HEALTHZ={ + 'live': liveness, + 'ready': readiness + }) + + app.register_blueprint(healthz, url_prefix='/healthz') + + from webui.service.main.routes import main + app.register_blueprint(main) + + from webui.service.service.routes import service + app.register_blueprint(service) + + from webui.service.device.routes import device + app.register_blueprint(device) + + app.jinja_env.globals.update(get_working_context=get_working_context) + + return app diff --git a/src/webui/service/__main__.py b/src/webui/service/__main__.py new file mode 100644 index 000000000..2107e139f --- /dev/null +++ b/src/webui/service/__main__.py @@ -0,0 +1,27 @@ +import os, sys, logging +from prometheus_client import start_http_server +from webui.service import create_app +from webui.Config import WEBUI_SERVICE_PORT, LOG_LEVEL, METRICS_PORT, HOST, SECRET_KEY, DEBUG + +def main(): + service_port = os.environ.get('WEBUISERVICE_SERVICE_PORT', WEBUI_SERVICE_PORT) + log_level = os.environ.get('LOG_LEVEL', LOG_LEVEL ) + metrics_port = os.environ.get('METRICS_PORT', METRICS_PORT ) + host = os.environ.get('HOST', HOST ) + debug = os.environ.get('DEBUG', DEBUG ) + + logging.basicConfig(level=log_level) + logger = logging.getLogger(__name__) + + logger.info('Starting...') + + start_http_server(metrics_port) + + app = create_app(use_config={'SECRET_KEY': SECRET_KEY}) + app.run(host=host, port=service_port, debug=debug) + + logger.info('Bye') + return 0 + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/webui/service/context/__init__.py b/src/webui/service/context/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/webui/service/context/routes.py b/src/webui/service/context/routes.py new file mode 100644 index 000000000..052a91db0 --- /dev/null +++ b/src/webui/service/context/routes.py @@ -0,0 +1,9 @@ +from flask import render_template, Blueprint, flash + +context = Blueprint('context', __name__) + +@context.route('/') +def home(): + flash('This is an info message', 'info') + flash('This is a danger message', 'danger') + return render_template('main/home.html') diff --git a/src/webui/service/device/__init__.py b/src/webui/service/device/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/webui/service/device/forms.py b/src/webui/service/device/forms.py new file mode 100644 index 000000000..8bfad0f59 --- /dev/null +++ b/src/webui/service/device/forms.py @@ -0,0 +1,41 @@ +# external imports +from flask_wtf import FlaskForm +from wtforms import StringField, SelectField, TextAreaField, SubmitField +from wtforms.validators import DataRequired, Length, NumberRange, Regexp, ValidationError + +from webui.utils.form_validators import key_value_validator +from webui.proto.context_pb2 import (DeviceDriverEnum, DeviceOperationalStatusEnum) + +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()]) + 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+)*$')]) + 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}.') diff --git a/src/webui/service/device/routes.py b/src/webui/service/device/routes.py new file mode 100644 index 000000000..b5a9399a6 --- /dev/null +++ b/src/webui/service/device/routes.py @@ -0,0 +1,121 @@ +from flask import render_template, Blueprint, flash, session, redirect +from device.client.DeviceClient import DeviceClient +from context.client.ContextClient import ContextClient +from webui.Config import (CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT, + DEVICE_SERVICE_ADDRESS, DEVICE_SERVICE_PORT) +from webui.proto.context_pb2 import (ContextId, DeviceList, DeviceId, + Device, DeviceDriverEnum, DeviceOperationalStatusEnum, + ConfigActionEnum, ConfigRule, TopologyIdList, TopologyList) +from webui.service.device.forms import AddDeviceForm + +device = Blueprint('device', __name__, url_prefix='/device') +context_client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT) +device_client: DeviceClient = DeviceClient(DEVICE_SERVICE_ADDRESS, DEVICE_SERVICE_PORT) + +@device.get('/') +def home(): + request: ContextId = ContextId() + request.context_uuid.uuid = session['context_uuid'] + context_client.connect() + response: DeviceList = context_client.ListDevices(request) + context_client.close() + return render_template('device/home.html', devices=response.devices, + dde=DeviceDriverEnum, + dose=DeviceOperationalStatusEnum) + +@device.route('add', methods=['GET', 'POST']) +def add(): + form = AddDeviceForm() + + request: ContextId = ContextId() + request.context_uuid.uuid = session['context_uuid'] + context_client.connect() + response: TopologyIdList = context_client.ListTopologyIds(request) + context_client.close() + + # listing enum values + form.operational_status.choices = [(-1, 'Select...')] + 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) + + if form.validate_on_submit(): + device: Device = Device() + 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.resource_key = parts[0].strip() + config_rule.resource_value = parts[1].strip() + device.device_config.config_rules.append(config_rule) + + 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)) + try: + device_client.connect() + response: DeviceId = device_client.AddDevice(device) + device_client.close() + + flash(f'New device was created with ID "{response.device_uuid.uuid}".', 'success') + return redirect('/device/') + 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) + +@device.route('detail/<device_uuid>', methods=['GET', 'POST']) +def detail(device_uuid: str): + request: DeviceId = DeviceId() + request.device_uuid.uuid = device_uuid + context_client.connect() + response: Device = context_client.GetDevice(request) + context_client.close() + return render_template('device/detail.html', device=response) + +@device.get('<device_uuid>/delete') +def delete(device_uuid): + try: + + # first, check if device exists! + # request: DeviceId = DeviceId() + # request.device_uuid.uuid = device_uuid + # response: Device = client.GetDevice(request) + # TODO: finalize implementation + + request: DeviceId = DeviceId() + request.device_uuid.uuid = device_uuid + device_client.connect() + response = device_client.DeleteDevice(request) + + device_client.close() + + flash('Device deleted successfully!', 'success') + except Exception as e: + flash(f'Problem deleting the device. {e.details()}', 'danger') + + return redirect('/device/') diff --git a/src/webui/service/main/__init__.py b/src/webui/service/main/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/webui/service/main/forms.py b/src/webui/service/main/forms.py new file mode 100644 index 000000000..aacdbf252 --- /dev/null +++ b/src/webui/service/main/forms.py @@ -0,0 +1,11 @@ +# external imports +from flask_wtf import FlaskForm +from wtforms import SelectField, SubmitField +from wtforms.validators import DataRequired, Length + + +class ContextForm(FlaskForm): + context = SelectField('Context', + choices=[], + validators=[DataRequired(), Length(min=1)]) + submit = SubmitField('Select') diff --git a/src/webui/service/main/routes.py b/src/webui/service/main/routes.py new file mode 100644 index 000000000..08ccdea4a --- /dev/null +++ b/src/webui/service/main/routes.py @@ -0,0 +1,37 @@ +import logging +import sys +from flask import render_template, Blueprint, flash, session +from webui.Config import CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT +from context.client.ContextClient import ContextClient +from webui.proto.context_pb2 import Empty +from webui.service.main.forms import ContextForm + +main = Blueprint('main', __name__) + +context_client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT) + +logger = logging.getLogger(__name__) + +@main.route('/', methods=['GET', 'POST']) +def home(): + # flash('This is an info message', 'info') + # flash('This is a danger message', 'danger') + context_client.connect() + response = context_client.ListContextIds(Empty()) + context_client.close() + context_form: ContextForm = ContextForm() + context_form.context.choices.append(('', 'Select...')) + for context in response.context_ids: + context_form.context.choices.append((context.context_uuid.uuid, context.context_uuid)) + if context_form.validate_on_submit(): + session['context_uuid'] = context_form.context.data + flash(f'The context was successfully set to `{context_form.context.data}`.', 'success') + if 'context_uuid' in session: + context_form.context.data = session['context_uuid'] + + return render_template('main/home.html', context_form=context_form) + + +@main.get('/about') +def about(): + return render_template('main/about.html') diff --git a/src/webui/service/service/__init__.py b/src/webui/service/service/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/webui/service/service/routes.py b/src/webui/service/service/routes.py new file mode 100644 index 000000000..c88d89789 --- /dev/null +++ b/src/webui/service/service/routes.py @@ -0,0 +1,39 @@ +from flask import render_template, Blueprint, flash, session +from webui.Config import CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT +from context.client.ContextClient import ContextClient +from webui.proto.context_pb2 import ContextId, ServiceList, ServiceTypeEnum, ServiceStatusEnum, ConfigActionEnum + + +service = Blueprint('service', __name__, url_prefix='/service') + +context_client: ContextClient = ContextClient(CONTEXT_SERVICE_ADDRESS, CONTEXT_SERVICE_PORT) + +@service.get('/') +def home(): + # flash('This is an info message', 'info') + # flash('This is a danger message', 'danger') + + request: ContextId = ContextId() + request.context_uuid.uuid = session['context_uuid'] + context_client.connect() + service_list: ServiceList = context_client.ListServices(request) + # print(service_list) + + context_client.close() + return render_template('service/home.html', services=service_list.services, + ste=ServiceTypeEnum, + sse=ServiceStatusEnum) + + +@service.route('add', methods=['GET', 'POST']) +def add(): + flash('Add service route called', 'danger') + raise NotImplementedError() + return render_template('service/home.html') + + +@service.get('detail/<service_uuid>') +def detail(service_uuid: str): + flash('Detail service route called', 'danger') + raise NotImplementedError() + return render_template('service/home.html') diff --git a/src/webui/service/static/partners.png b/src/webui/service/static/partners.png new file mode 100644 index 0000000000000000000000000000000000000000..f88680212f68cdb4c17ad0de55b9d22ef9276a23 GIT binary patch literal 76488 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 AtpET3 literal 0 HcmV?d00001 diff --git a/src/webui/service/static/site.js b/src/webui/service/static/site.js new file mode 100644 index 000000000..e06cea8b0 --- /dev/null +++ b/src/webui/service/static/site.js @@ -0,0 +1,4 @@ +var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) +var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl) +}) diff --git a/src/webui/service/templates/base.html b/src/webui/service/templates/base.html new file mode 100644 index 000000000..6ada3c850 --- /dev/null +++ b/src/webui/service/templates/base.html @@ -0,0 +1,119 @@ +<!doctype html> +<html lang="en"> + <head> + <!-- Required meta tags --> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + + <link rel="shortcut icon" href="https://teraflow-h2020.eu/sites/teraflow/files/public/favicon.png" type="image/png" /> + + <!-- Bootstrap CSS --> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-uWxY/CJNBR+1zjPWmfnSnVxwRheevXITnMqoEIeG1LJrdI0GlVs/9cVSyPYXdcSF" crossorigin="anonymous"> + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css"> + + <title>TeraFlow OFC 2022 Demo</title> + </head> + <body> + <div id="teraflow-branding" style="width: 260px; margin: 7px;"> + <a href="/" title="Home" rel="home" id="main-logo" class="site-logo site-logo-pages"> + <svg id="Capa_1" data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 436.3 132.1"><defs><style>.cls-1{fill:#36a9e1;}.cls-2{fill:#1d71b8;}.cls-3{fill:none;stroke-width:2.52px;}.cls-10,.cls-3,.cls-4,.cls-5,.cls-7,.cls-8,.cls-9{stroke:#0f77b6;}.cls-3,.cls-4,.cls-8{stroke-miterlimit:10;}.cls-10,.cls-4,.cls-5,.cls-7,.cls-8,.cls-9{fill:#fff;}.cls-4{stroke-width:0.73px;}.cls-5,.cls-7{stroke-miterlimit:10;}.cls-5{stroke-width:0.75px;}.cls-6{fill:#0f77b6;}.cls-7{stroke-width:0.72px;}.cls-8{stroke-width:0.7px;}.cls-9{stroke-miterlimit:10;stroke-width:0.69px;}.cls-10{stroke-miterlimit:10;stroke-width:0.7px;}</style></defs><path class="cls-1" d="M96,57V51.3h44.1V57H121v52.3h-5.9V57Z"></path><path class="cls-1" d="M168.9,95.1l4.7,2.4a26,26,0,0,1-5.3,7.3,22.27,22.27,0,0,1-6.7,4.2,22.64,22.64,0,0,1-8.5,1.4c-7,0-12.5-2.3-16.4-6.9a23.53,23.53,0,0,1-5.9-15.6,23,23,0,0,1,5-14.5c4.2-5.4,9.9-8.1,17-8.1,7.3,0,13.2,2.8,17.5,8.3,3.1,3.9,4.7,8.8,4.7,14.7H136.4a17.48,17.48,0,0,0,4.8,12.3,15.26,15.26,0,0,0,11.4,4.8,20,20,0,0,0,6.4-1.1,19.3,19.3,0,0,0,5.3-3A33.07,33.07,0,0,0,168.9,95.1Zm0-11.6a18.66,18.66,0,0,0-3.2-7.1,15.25,15.25,0,0,0-5.6-4.3,16.87,16.87,0,0,0-7.3-1.6,16.06,16.06,0,0,0-10.9,4.1,18.15,18.15,0,0,0-5,8.9Z"></path><path class="cls-1" d="M182,66.4h5.6v6.3a20,20,0,0,1,5.3-5.5,10.67,10.67,0,0,1,5.8-1.8,9.87,9.87,0,0,1,4.9,1.5l-2.9,4.7a7.52,7.52,0,0,0-2.9-.7,8.09,8.09,0,0,0-5.3,2.3,14.64,14.64,0,0,0-3.9,7c-.7,2.4-1,7.4-1,14.8v14.5H182Z"></path><path class="cls-1" d="M246.2,66.4v42.9h-5.4V102a23.11,23.11,0,0,1-7.8,6.3,21.23,21.23,0,0,1-9.4,2.1,21,21,0,0,1-15.6-6.6,23.07,23.07,0,0,1,.1-32,21.23,21.23,0,0,1,15.7-6.6,20,20,0,0,1,17.1,8.9V66.2h5.3Zm-22.1,4.2a16.67,16.67,0,0,0-8.5,2.3,15.93,15.93,0,0,0-6.2,6.4,17.68,17.68,0,0,0-2.3,8.7,18.26,18.26,0,0,0,2.3,8.7,15.93,15.93,0,0,0,6.2,6.4,16.58,16.58,0,0,0,8.4,2.3,17.59,17.59,0,0,0,8.6-2.3,15.42,15.42,0,0,0,6.2-6.2,17.17,17.17,0,0,0,2.2-8.8,16.73,16.73,0,0,0-4.9-12.4A15.8,15.8,0,0,0,224.1,70.6Z"></path><path class="cls-2" d="M259.5,51.3h29.1V57H265.3V75.2h23.3v5.7H265.3v28.5h-5.8V51.3Z"></path><path class="cls-2" d="M296.9,49.9h5.5v59.5h-5.5Z"></path><path class="cls-2" d="M330.5,65.3a21.1,21.1,0,0,1,16.4,7.2A22.55,22.55,0,0,1,352.8,88a22.24,22.24,0,0,1-6.3,15.7c-4.2,4.5-9.5,6.7-16.1,6.7s-12-2.2-16.1-6.7A22.24,22.24,0,0,1,308,88a22.73,22.73,0,0,1,5.9-15.5A21.81,21.81,0,0,1,330.5,65.3Zm0,5.4a15.83,15.83,0,0,0-11.8,5.1,17,17,0,0,0-4.9,12.3,17.68,17.68,0,0,0,2.3,8.7,15.19,15.19,0,0,0,6.1,6.2,16.48,16.48,0,0,0,8.4,2.2A16,16,0,0,0,339,103a15.82,15.82,0,0,0,6.1-6.2,17.68,17.68,0,0,0,2.3-8.7,17.07,17.07,0,0,0-5-12.3A16.2,16.2,0,0,0,330.5,70.7Z"></path><path class="cls-2" d="M351.2,66.4h5.7L370,97.6l13.7-31.1h1l13.8,31.1,13.4-31.1h5.7L399,109.3h-1L384.3,78.6l-13.7,30.7h-1Z"></path><polyline class="cls-3" points="51 105 51 41.2 27 41.2"></polyline><polyline class="cls-3" points="38.1 33.8 56.4 33.8 56.4 93"></polyline><polyline class="cls-3" points="79.9 33.8 61.5 33.8 61.5 79.2"></polyline><polyline class="cls-3" points="90.7 41.2 66.7 41.2 66.7 105"></polyline><line class="cls-3" x1="83.1" y1="62.6" x2="66.7" y2="62.6"></line><circle class="cls-4" cx="27" cy="41.2" r="5.3"></circle><path class="cls-1" d="M23.3,41.2a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,23.3,41.2Z"></path><circle class="cls-5" cx="51" cy="105" r="5.4"></circle><path class="cls-1" d="M47.3,105a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,47.3,105Z"></path><circle class="cls-6" cx="56.36" cy="93.02" r="3.4"></circle><circle class="cls-6" cx="61.5" cy="79.2" r="2.8"></circle><circle class="cls-7" cx="66.7" cy="105.01" r="5.3"></circle><path class="cls-1" d="M63,105a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,63,105Z"></path><circle class="cls-8" cx="90.7" cy="41.2" r="5.1"></circle><path class="cls-1" d="M87,41.2a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,87,41.2Z"></path><circle class="cls-8" cx="84.7" cy="62.6" r="5.1"></circle><path class="cls-1" d="M81,62.6a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,81,62.6Z"></path><line class="cls-3" x1="34.8" y1="62.6" x2="51.1" y2="62.6"></line><circle class="cls-8" cx="33.1" cy="62.6" r="5.1"></circle><path class="cls-1" d="M36.9,62.6a3.8,3.8,0,1,1-3.8-3.8A3.8,3.8,0,0,1,36.9,62.6Z"></path><line class="cls-3" x1="23.7" y1="26.7" x2="94.1" y2="26.7"></line><circle class="cls-9" cx="94.09" cy="26.67" r="5"></circle><path class="cls-1" d="M90.3,26.7a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,90.3,26.7Z"></path><circle class="cls-6" cx="78" cy="33.8" r="3.8"></circle><circle class="cls-6" cx="40" cy="33.8" r="3.8"></circle><circle class="cls-10" cx="23.71" cy="26.71" r="5.1"></circle><path class="cls-1" d="M20,26.7a3.8,3.8,0,1,0,3.8-3.8A3.8,3.8,0,0,0,20,26.7Z"></path></svg> + </a> + </div> + + <nav class="navbar navbar-expand-lg navbar-dark bg-primary" style="margin-bottom: 10px;"> + <div class="container-fluid"> + <a class="navbar-brand" href="#"> + <img src="https://teraflow-h2020.eu/sites/teraflow/files/public/favicon.png" alt="" width="30" height="24" class="d-inline-block align-text-top"/> + TeraFlow + </a> + <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarColor02" aria-controls="navbarColor02" aria-expanded="false" aria-label="Toggle navigation"> + <span class="navbar-toggler-icon"></span> + </button> + <div class="collapse navbar-collapse" id="navbarColor02"> + <ul class="navbar-nav me-auto mb-2 mb-lg-0"> + <li class="nav-item"> + {% if request.path == '/' %} + <a class="nav-link active" aria-current="page" href="{{ url_for('main.home') }}">Home</a> + {% else %} + <a class="nav-link" href="{{ url_for('main.home') }}">Home</a> + {% endif %} + </li> + <li class="nav-item"> + {% if '/service/' in request.path %} + <a class="nav-link active" aria-current="page" href="{{ url_for('service.home') }}">Service</a> + {% else %} + <a class="nav-link" href="{{ url_for('service.home') }}">Service</a> + {% endif %} + <!-- <a class="nav-link" href="{{ url_for('service.home') }}">Service</a> --> + </li> + <li class="nav-item"> + {% if '/device/' in request.path %} + <a class="nav-link active" aria-current="page" href="{{ url_for('device.home') }}">Device</a> + {% else %} + <a class="nav-link" href="{{ url_for('device.home') }}">Device</a> + {% endif %} + <!-- <a class="nav-link" href="{{ url_for('service.home') }}">Service</a> --> + </li> + <!-- <li class="nav-item"> + <a class="nav-link" href="#">Compute</a> + </li> + <li class="nav-item"> + <a class="nav-link" href="#">Context</a> + </li> + + <li class="nav-item"> + <a class="nav-link" href="#">Monitoring</a> + </li> --> + <li class="nav-item"> + <a class="nav-link" href="{{ url_for('main.about') }}">About</a> + </li> + </ul> + <span class="navbar-text" style="color: #fff;"> + Current context: <b>{{ get_working_context() }}</b> + </span> + </div> + </div> + </nav> + + <main class="container"> + <div class="row"> + <div class="col-md-12"> + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} + {% for category, message in messages %} + <div class="alert alert-{{ category }} alert-dismissible fade show" role="alert"> + {{ message }} + <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> + </div> + + {% endfor %} + {% endif %} + {% endwith %} + </div> + </div> + <div class="bg-light p-5 rounded"> + {% block content %}{% endblock %} + </div> + </main> + + <footer class="footer" style="background-color: darkgrey;"> + <div class="row"> + <div class="col-md-12"> + <p class="text-muted text-center" style="color: white;">© 2021-2023</p> + </div> + </div> + </footer> + + <!-- Optional JavaScript; choose one of the two! --> + + <!-- Option 1: Bootstrap Bundle with Popper --> + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-kQtW33rZJAHjgefvhyyzcGF3C5TFyBQBA13V1RKPf4uH+bwyzQxZ6CmMZHmNBEfJ" crossorigin="anonymous"></script> + <!-- <script src="{{ url_for('static', filename='site.js') }}"/> --> + + <!-- Option 2: Separate Popper and Bootstrap JS --> + <!-- + <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.10.2/dist/umd/popper.min.js" integrity="sha384-7+zCNj/IqJ95wo16oMtfsKbZ9ccEh31eOz1HGyDuCQ6wgnyJNSYdrPa03rtR1zdB" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/js/bootstrap.min.js" integrity="sha384-PsUw7Xwds7x08Ew3exXhqzbhuEYmA2xnwc8BuD6SEr+UmEHlX8/MCltYEodzWA4u" crossorigin="anonymous"></script> + --> + </body> +</html> \ No newline at end of file diff --git a/src/webui/service/templates/context/home.html b/src/webui/service/templates/context/home.html new file mode 100644 index 000000000..c389ea658 --- /dev/null +++ b/src/webui/service/templates/context/home.html @@ -0,0 +1,79 @@ +{% extends 'base.html' %} + +{% block content %} + <h1>Service</h1> + + <div class="row"> + <div class="col"> + <a href="{{ url_for('service.add') }}" class="btn btn-primary" style="margin-bottom: 10px;"> + <i class="bi bi-plus"></i> + Add New Service + </a> + </div> + <div class="col"> + {{ services | length }} services found + </div> + <div class="col"> + <form> + <div class="input-group"> + <input type="text" aria-label="Search" placeholder="Search..." class="form-control"/> + <button type="submit" class="btn btn-primary">Search</button> + </div> + </form> + </div> + </div> + + + <table class="table table-striped"> + <thead> + <tr> + <th scope="col">#</th> + <th scope="col">Type</th> + <th scope="col">End points</th> + <th scope="col">Constraints</th> + <th scope="col">State</th> + <th scope="col">Configuration</th> + <th scope="col"></th> + </tr> + </thead> + <tbody> + {% if services %} + {% for service in services %} + <tr> + <td><a href="{{ url_for('service.detail', contextUuid=service.cs_id.contextId.contextUuid.uuid, serviceUuid=service.cs_id.cs_id.uuid) }}">{{ service.cs_id.contextId.contextUuid.uuid }} / {{ service.cs_id.cs_id.uuid }}</a></td> + <td>{{ service.serviceType }}</td> + <td> + <ul> + {% for end_point in service.endpointList %} + <li>{{ end_point }}</li> + {% endfor %} + </ul> + </td> + <td> + <ul> + {% for constraint in service.constraint %} + <li>{{ constraint }}</li> + {% endfor %} + </ul> + </td> + <td>{{ service.serviceState }}</td> + <td>{{ service.serviceConfig }}</td> + <td> + <a href="{{ url_for('service.detail', contextUuid=service.cs_id.contextId.contextUuid.uuid, serviceUuid=service.cs_id.cs_id.uuid) }}"> + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16"> + <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/> + <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/> + </svg> + </a> + </td> + </tr> + {% endfor %} + {% else %} + <tr> + <td colspan="7">No services found</td> + </tr> + {% endif %} + </tbody> + </table> + +{% endblock %} \ No newline at end of file diff --git a/src/webui/service/templates/device/add.html b/src/webui/service/templates/device/add.html new file mode 100644 index 000000000..dae31a376 --- /dev/null +++ b/src/webui/service/templates/device/add.html @@ -0,0 +1,93 @@ +{% extends 'base.html' %} + +{% block content %} + <h1>Add New Device</h1> + + <form id="add_device" method="POST"> + {{ form.hidden_tag() }} + <fieldset> + <div class="row mb-3"> + {{ form.device_id.label(class="col-sm-2 col-form-label") }} + <div class="col-sm-10"> + {% if form.device_id.errors %} + {{ form.device_id(class="form-control is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.device_id.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.device_id(class="form-control") }} + {% endif %} + </div> + </div> + <div class="row mb-3"> + {{ form.device_type.label(class="col-sm-2 col-form-label") }} + <div class="col-sm-10"> + {% if form.device_type.errors %} + {{ form.device_type(class="form-control is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.device_type.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.device_type(class="form-control") }} + {% endif %} + </div> + </div> + <div class="row mb-3"> + {{ form.operational_status.label(class="col-sm-2 col-form-label") }} + <div class="col-sm-10"> + {% if form.operational_status.errors %} + {{ form.operational_status(class="form-control is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.operational_status.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.operational_status(class="form-control") }} + {% endif %} + </div> + </div> + <div class="row mb-3"> + {{ form.device_config.label(class="col-sm-2 col-form-label") }} + <div class="col-sm-10"> + {% if form.device_config.errors %} + {{ form.device_config(class="form-control is-invalid", rows=5) }} + <div class="invalid-feedback"> + {% for error in form.device_config.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.device_config(class="form-control", rows=5) }} + {% endif %} + </div> + <div id="device_config_help" class="form-text">The device configurations should follow a <i>key=value</i> format, one configuration per line.</div> + </div> + <div class="row mb-3"> + {{ form.device_drivers.label(class="col-sm-2 col-form-label") }} + <div class="col-sm-10"> + {% if form.device_drivers.errors %} + {{ form.device_drivers(class="form-control is-invalid", rows=5) }} + <div class="invalid-feedback"> + {% for error in form.device_drivers.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ form.device_drivers(class="form-control", rows=5) }} + {% endif %} + </div> + <div id="device_drivers_help" class="form-text"> + List the device drivers by their numerical ID, separated by commas, without spaces between them. Numerical IDs: {{ device_driver_ids }}. + </div> + </div> + <div class="row mb-3"> + <button type="submit" class="btn btn-primary">{{ submit_text }}</button> + </div> + </fieldset> + </form> +{% 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 new file mode 100644 index 000000000..c090e13b5 --- /dev/null +++ b/src/webui/service/templates/device/detail.html @@ -0,0 +1,73 @@ +{% extends 'base.html' %} + +{% block content %} + <h1>Device {{ device.device_id.device_uuid.uuid }}</h1> + + <div class="row mb-3"> + <div class="col-sm-3"> + <button type="button" class="btn btn-success" onclick="window.location.href = '/device/'"><i class="bi bi-box-arrow-in-left"></i>Back to device list</button> + </div> + <div class="col-sm-3"> + <a id="update" class="btn btn-secondary" href="#"><i class="bi bi-pencil-square"></i>Update</a> + </div> + <div class="col-sm-3"> + <!-- <button type="button" class="btn btn-danger"><i class="bi bi-x-square"></i>Delete device</button> --> + <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal"> + <i class="bi bi-x-square"></i>Delete device + </button> + </div> + </div> + + <div class="row mb-3"> + <b>UUID:</b> + <div class="col-sm-10"> + {{ device.device_id.device_uuid.uuid }} + </div> + </div> + <div class="row mb-3"> + <b>Type:</b> + <div class="col-sm-10"> + {{ device.device_type }} + </div> + </div> + <div class="row mb-3"> + <b>Configurations:</b> + <div class="col-sm-10"> + <ul> + {% for config in device.device_config.config_rules %} + <li>{{ config.resource_key }}: {{ config.resource_value }}</li> + {% endfor %} + </ul> + </div> + </div> + <div class="row mb-3"> + <b>Endpoints:</b> + <div class="col-sm-10"> + <ul> + {% for endpoint in device.device_endpoints %} + <li>{{ endpoint.endpoint_id.endpoint_uuid.uuid }}: {{ endpoint.endpoint_type }}</li> + {% endfor %} + </ul> + </div> + </div> + + <!-- Modal --> +<div class="modal fade" id="deleteModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="staticBackdropLabel">Delete device?</h5> + <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> + </div> + <div class="modal-body"> + Are you sure you want to delete the device "{{ device.device_id.device_uuid.uuid }}"? + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">No</button> + <a type="button" class="btn btn-danger" href="{{ url_for('device.delete', device_uuid=device.device_id.device_uuid.uuid) }}"><i class="bi bi-exclamation-diamond"></i>Yes</a> + </div> + </div> + </div> + </div> + +{% endblock %} \ No newline at end of file diff --git a/src/webui/service/templates/device/home.html b/src/webui/service/templates/device/home.html new file mode 100644 index 000000000..6010ed6a9 --- /dev/null +++ b/src/webui/service/templates/device/home.html @@ -0,0 +1,93 @@ +{% extends 'base.html' %} + +{% block content %} + <h1>Devices</h1> + + <div class="row"> + <!-- <div class="col"> + <a href="{{ url_for('device.add') }}" class="btn btn-primary" style="margin-bottom: 10px;"> + <i class="bi bi-plus"></i> + Add New Device + </a> + </div> --> + <div class="col"> + {{ devices | length }} devices found in context <i>{{ session['context_uuid'] }}</i> + </div> + <!-- <div class="col"> + <form> + <div class="input-group"> + <input type="text" aria-label="Search" placeholder="Search..." class="form-control"/> + <button type="submit" class="btn btn-primary">Search</button> + </div> + </form> + </div> --> + </div> + + <table class="table table-striped table-hover"> + <thead> + <tr> + <th scope="col">#</th> + <th scope="col">Type</th> + <th scope="col">Endpoints</th> + <th scope="col">Drivers</th> + <th scope="col">Status</th> + <th scope="col">Configuration</th> + <!-- <th scope="col"></th> --> + </tr> + </thead> + <tbody> + {% if devices %} + {% for device in devices %} + <tr> + <td> + <!-- <a href="{{ url_for('device.detail', device_uuid=device.device_id.device_uuid.uuid) }}"> --> + {{ device.device_id.device_uuid.uuid }} + <!-- </a> --> + </td> + <td> + {{ device.device_type }} + </td> + <td> + <ul> + {% for end_point in device.device_endpoints %} + <li>{{ end_point.endpoint_id.endpoint_uuid.uuid }}</li> + {% endfor %} + </ul> + </td> + <td> + <ul> + {% for driver in device.device_drivers %} + <li>{{ dde.Name(driver) }}</li> + {% endfor %} + </ul> + </td> + <td>{{ dose.Name(device.device_operational_status) }}</td> + <td> + <ul> + {% for config in device.device_config.config_rules %} + <li> + Key: {{ config.resource_key }}<br/> + Value: {{ config.resource_value }} + </li> + {% endfor %} + </ul> + </td> + <!-- <td> + <a href="{{ url_for('device.detail', device_uuid=device.device_id.device_uuid.uuid) }}"> + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16"> + <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/> + <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/> + </svg> + </a> + </td> --> + </tr> + {% endfor %} + {% else %} + <tr> + <td colspan="7">No devices found</td> + </tr> + {% endif %} + </tbody> + </table> + +{% endblock %} \ No newline at end of file diff --git a/src/webui/service/templates/main/about.html b/src/webui/service/templates/main/about.html new file mode 100644 index 000000000..9ac1e29e8 --- /dev/null +++ b/src/webui/service/templates/main/about.html @@ -0,0 +1,9 @@ +{% extends 'base.html' %} +{% block content %} + <h1>TeraFlow OS</h1> + + <p>For more information, visit the <a href="https://teraflow-h2020.eu/" target="_newtf">TeraFlow H2020 webpage</a>.</p> + + <img alt="Consortium" src="{{ url_for('static', filename='partners.png') }}"/> + +{% endblock %} \ No newline at end of file diff --git a/src/webui/service/templates/main/home.html b/src/webui/service/templates/main/home.html new file mode 100644 index 000000000..2bda77eb0 --- /dev/null +++ b/src/webui/service/templates/main/home.html @@ -0,0 +1,37 @@ +{% extends 'base.html' %} + +{% block content %} + <h1>This is the home page</h1> + <p>Here we have have several things.</p> + + {% for field, message in context_form.errors.items() %} + <div class="alert alert-dismissible fade show" role="alert"> + <b>{{ field }}</b>: {{ message }} + <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> + </div> + + {% endfor %} + + <h2>Select the working context</h2> + <form id="select_context" method="POST"> + {{ context_form.hidden_tag() }} + <fieldset class="form-group"> + <div class="input-group mb-3"> + + {% if context_form.context.errors %} + {{ context_form.context(class="form-select is-invalid") }} + <div class="invalid-feedback"> + {% for error in context_form.context.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + {{ context_form.context(class="form-select") }} + {% endif %} + + {{ context_form.submit(class='btn btn-primary') }} + </div> + </fieldset> + </form> + +{% endblock %} \ No newline at end of file diff --git a/src/webui/service/templates/service/home.html b/src/webui/service/templates/service/home.html new file mode 100644 index 000000000..648ce99bd --- /dev/null +++ b/src/webui/service/templates/service/home.html @@ -0,0 +1,95 @@ +{% extends 'base.html' %} + +{% block content %} + <h1>Services</h1> + + <div class="row"> + <!-- <div class="col"> + <a href="{{ url_for('service.add') }}" class="btn btn-primary" style="margin-bottom: 10px;"> + <i class="bi bi-plus"></i> + Add New Service + </a> + </div> --> + <div class="col"> + {{ services | length }} services found in context <i>{{ session['context_uuid'] }}</i> + </div> + <!-- <div class="col"> + <form> + <div class="input-group"> + <input type="text" aria-label="Search" placeholder="Search..." class="form-control"/> + <button type="submit" class="btn btn-primary">Search</button> + </div> + </form> + </div> --> + </div> + + + <table class="table table-striped table-hover"> + <thead> + <tr> + <th scope="col">#</th> + <th scope="col">Type</th> + <th scope="col">End points</th> + <th scope="col">Constraints</th> + <th scope="col">Status</th> + <th scope="col">Configuration</th> + <!-- <th scope="col"></th> --> + </tr> + </thead> + <tbody> + {% if services %} + {% for service in services %} + <tr> + <td> + <!-- <a href="{{ url_for('service.detail', service_uuid=service.service_id.service_uuid.uuid) }}"> --> + {{ service.service_id.service_uuid.uuid }} + <!-- </a> --> + </td> + <td> + {{ ste.Name(service.service_type) }} + </td> + <td> + <ul> + {% for end_point in service.service_endpoint_ids %} + <li>{{ end_point.device_id.device_uuid.uuid }} / {{ end_point.endpoint_uuid.uuid }}</li> + {% endfor %} + </ul> + </td> + <td> + <ul> + {% for constraint in service.service_constraints %} + <li>{{ constraint.constraint_type }}: {{ constraint.constraint_value }}</li> + {% endfor %} + </ul> + </td> + <td>{{ sse.Name(service.service_status.service_status) }}</td> + <td> + <ul> + {% for rule in service.service_config.config_rules %} + <li> + Key: {{ rule.resource_key }} + <br/> + Value: {{ rule.resource_value }} + </li> + {% endfor %} + </ul> + </td> + <!-- <td> + <a href="{{ url_for('service.detail', service_uuid=service.service_id.service_uuid.uuid) }}"> + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16"> + <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/> + <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/> + </svg> + </a> + </td> --> + </tr> + {% endfor %} + {% else %} + <tr> + <td colspan="7">No services found</td> + </tr> + {% endif %} + </tbody> + </table> + +{% endblock %} \ No newline at end of file diff --git a/src/webui/tests/__init__.py b/src/webui/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/webui/tests/test_unitary.py b/src/webui/tests/test_unitary.py new file mode 100644 index 000000000..0612c9d05 --- /dev/null +++ b/src/webui/tests/test_unitary.py @@ -0,0 +1,156 @@ +# import pytest +from flask_unittest import ClientTestCase +from unittest import mock +from flask.testing import FlaskClient +from flask.app import Flask +from flask.helpers import url_for +# from device.client.DeviceClient import DeviceClient +from webui.service import create_app +from webui.proto.context_pb2 import Empty, DeviceId, DeviceList, TopologyIdList + +class TestWebUI(ClientTestCase): + app = create_app(use_config={'TESTING': True, + 'SERVER_NAME': 'localhost.localdomain', + 'SECRET_KEY': '>s&}24@{]]#k3&^5$f3#?6?h3{W@[}/7z}2pa]>{3&5%RP<)[(', + 'WTF_CSRF_ENABLED': False}) + + def setUp(self, client: FlaskClient) -> None: + + self.mocker_delete_device = mock.patch('webui.service.device.routes.device_client.DeleteDevice') + self.mocker_delete_device.return_value = Empty() + self.mocker_delete_device.start() + self.addCleanup(self.mocker_delete_device.stop) + + self.mocker_list_devices = mock.patch('webui.service.device.routes.context_client.ListDevices') + self.mocker_list_devices.return_value = DeviceList() # returns an empty list + self.mocker_list_devices.start() + self.addCleanup(self.mocker_list_devices.stop) + + self.mocker_add_device = mock.patch('webui.service.device.routes.device_client.AddDevice') + self.mocker_add_device.return_value = DeviceId() + self.mocker_add_device.start() + self.addCleanup(self.mocker_add_device.stop) + + self.mocker_list_topology_ids = mock.patch('webui.service.device.routes.context_client.ListTopologyIds') + self.mocker_list_topology_ids.return_value = TopologyIdList() + self.mocker_list_topology_ids.start() + self.addCleanup(self.mocker_list_topology_ids.stop) + + return super().setUp(client) + + def tearDown(self, client: FlaskClient) -> None: + mock.patch.stopall() + return super().tearDown(client) + + def test_routes(self, client): + with self.app.app_context(): + url_for('main.home') + url_for('service.home') + url_for('device.home') + url_for('main.about') + + def test_device_add_action_success(self, client): + with client.session_transaction() as sess: + sess['context_uuid'] = 'admin' + DEVICE_EMU = { + 'device_id': 'EMULATED', + 'device_type': 'emulated', + 'device_config': '', + 'operational_status': 1, + 'device_drivers': 0, + 'device_endpoints': [], + } + rv = client.post('/device/add', data=DEVICE_EMU, follow_redirects=True) + self.assertInResponse(b'success', rv) + + def test_device_delete_action(self, client): + with client.session_transaction() as sess: + sess['context_uuid'] = 'admin' + + rv = client.get('/device/EMULATED/delete', follow_redirects=True) + # mocked_list.assert_called() + # mocked_delete.assert_called() + self.assertInResponse(b'success', rv) + + def test_service_up(self, client): + pass + + + +# def test_service_up(client): +# rw = client.get('/') +# assert rw.status_code == 200, 'Service is not up!' + +# def test_home_page(client): +# rw = client.get('/') +# assert rw.status_code == 200, 'Error in the home page!' +# assert b'Select the working context' in rw.data + +# def test_service_home_page(client): +# with client.session_transaction() as sess: +# sess['context_uuid'] = 'admin' +# rw = client.get('/service/') +# assert rw.status_code == 200 +# assert b'Services' in rw.data +# assert b'Add New Service' in rw.data + +# def test_device_home_page(client): +# with client.session_transaction() as sess: +# sess['context_uuid'] = 'admin' +# rw = client.get('/device/') +# assert rw.status_code == 200 +# assert b'Devices' in rw.data +# assert b'Add New Device' in rw.data + +# @pytest.mark.parametrize('device_id', ( +# 'DEV1', +# 'DEV2', +# 'DEV3', +# )) +# def test_device_detail_page(client, device_id): +# with client.session_transaction() as sess: +# sess['context_uuid'] = 'admin' +# rw = client.get(f'/device/detail/{device_id}') +# assert rw.status_code == 200 +# assert b'Device' in rw.data +# assert device_id in rw.data.decode() +# assert b'Endpoints' in rw.data, 'Missing endpoint information on the device detail page.' +# # assert b'Add New Device' in rw.data + +# def test_device_add_page(client): +# with client.session_transaction() as sess: +# sess['context_uuid'] = 'admin' +# rw = client.get('/device/add') +# assert rw.status_code == 200 +# assert b'Add New Device' in rw.data +# assert b'Operational Status' in rw.data, 'Form is not correctly implemented.' +# assert b'Type' in rw.data, 'Form is not correctly implemented.' +# assert b'Configurations' in rw.data, 'Form is not correctly implemented.' +# assert b'Drivers' in rw.data, 'Form is not correctly implemented.' + +# def test_device_add_action(client): +# with client.session_transaction() as sess: +# sess['context_uuid'] = 'admin' +# DEVICE_EMU = { +# 'device_id': 'EMULATED', +# 'device_type': 'emulated', +# 'device_config': '', +# 'operational_status': 1, +# 'device_drivers': 0, +# 'device_endpoints': [], +# } +# with mock.patch('webui.service.device.routes.device_client.AddDevice') as mocked_add: +# mocked_add.return_value = DeviceId() +# rw = client.post('/device/add', data=DEVICE_EMU, follow_redirects=True) +# assert b'success' in rw.data + +# def test_device_delete_action(client): +# with client.session_transaction() as sess: +# sess['context_uuid'] = 'admin' +# with mock.patch('webui.service.device.routes.device_client.DeleteDevice') as mocked_delete,\ +# mock.patch('webui.service.device.routes.context_client.ListDevices') as mocked_list: +# mocked_list.return_value = DeviceList() # returns an empty list +# rw = client.get('/device/EMULATED/delete', follow_redirects=True) +# mocked_list.assert_called() +# mocked_delete.assert_called() +# assert b'success' in rw.data diff --git a/src/webui/utils/__init__.py b/src/webui/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/webui/utils/form_validators.py b/src/webui/utils/form_validators.py new file mode 100644 index 000000000..e339fdcc0 --- /dev/null +++ b/src/webui/utils/form_validators.py @@ -0,0 +1,14 @@ +from wtforms.validators import ValidationError + +def key_value_validator(): + def _validate(form, field): + if len(field.data) > 0: + if '\n' not in field.data: # case in which there is only one configuration + if '=' not in field.data: + raise ValidationError(f'Configuration "{field.data}" does not follow the key=value pattern.') + else: # case in which there are several configurations + configurations = field.data.split('\n') + for configutation in configurations: + if '=' not in configutation: + raise ValidationError(f'Configuration "{configutation}" does not follow the key=value pattern.') + return _validate -- GitLab