Loading manifests/webuiservice.yaml +40 −40 Original line number Diff line number Diff line Loading @@ -60,43 +60,43 @@ spec: limits: cpu: 700m memory: 1024Mi - name: grafana image: grafana/grafana:8.2.6 imagePullPolicy: IfNotPresent ports: - containerPort: 3000 name: http-grafana protocol: TCP env: - name: GF_SERVER_ROOT_URL value: "http://0.0.0.0:3000/grafana/" - name: GF_SERVER_SERVE_FROM_SUB_PATH value: "true" readinessProbe: failureThreshold: 3 httpGet: path: /robots.txt port: 3000 scheme: HTTP initialDelaySeconds: 10 periodSeconds: 30 successThreshold: 1 timeoutSeconds: 2 livenessProbe: failureThreshold: 3 initialDelaySeconds: 30 periodSeconds: 10 successThreshold: 1 tcpSocket: port: 3000 timeoutSeconds: 1 resources: requests: cpu: 250m memory: 750Mi limits: cpu: 700m memory: 1024Mi # - name: grafana # image: grafana/grafana:8.2.6 # imagePullPolicy: IfNotPresent # ports: # - containerPort: 3000 # name: http-grafana # protocol: TCP # env: # - name: GF_SERVER_ROOT_URL # value: "http://0.0.0.0:3000/grafana/" # - name: GF_SERVER_SERVE_FROM_SUB_PATH # value: "true" # readinessProbe: # failureThreshold: 3 # httpGet: # path: /robots.txt # port: 3000 # scheme: HTTP # initialDelaySeconds: 10 # periodSeconds: 30 # successThreshold: 1 # timeoutSeconds: 2 # livenessProbe: # failureThreshold: 3 # initialDelaySeconds: 30 # periodSeconds: 10 # successThreshold: 1 # tcpSocket: # port: 3000 # timeoutSeconds: 1 # resources: # requests: # cpu: 250m # memory: 750Mi # limits: # cpu: 700m # memory: 1024Mi --- apiVersion: v1 kind: Service Loading @@ -110,6 +110,6 @@ spec: - name: webui port: 8004 targetPort: 8004 - name: grafana port: 3000 targetPort: 3000 # - name: grafana # port: 3000 # targetPort: 3000 my_deploy.sh +0 −7 Original line number Diff line number Diff line # Set the URL of your local Docker registry where the images will be uploaded to. export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" # Set the list of components, separated by spaces, you want to build images for, and deploy. Loading @@ -11,12 +10,6 @@ export TFS_COMPONENTS="context device automation service compute monitoring webu # Set the tag you want to use for your images. export TFS_IMAGE_TAG="dev" # Set the name of the Kubernetes namespace to deploy to. export TFS_K8S_NAMESPACE="tfs" # Set additional manifest files to be applied after the deployment export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml" # Set the neew Grafana admin password export TFS_GRAFANA_PASSWORD="admin123+" src/webui/Dockerfile +2 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,8 @@ COPY --chown=webui:webui src/device/__init__.py device/__init__.py COPY --chown=webui:webui src/device/client/. device/client/ COPY --chown=webui:webui src/service/__init__.py service/__init__.py COPY --chown=webui:webui src/service/client/. service/client/ COPY --chown=webui:webui src/slice/__init__.py slice/__init__.py COPY --chown=webui:webui src/slice/client/. slice/client/ COPY --chown=webui:webui src/webui/. webui/ # Start the service Loading src/webui/service/__init__.py +4 −0 Original line number Diff line number Diff line Loading @@ -72,12 +72,16 @@ def create_app(use_config=None, web_app_root=None): from webui.service.service.routes import service app.register_blueprint(service) from webui.service.slice.routes import slice app.register_blueprint(slice) from webui.service.device.routes import device app.register_blueprint(device) from webui.service.link.routes import link app.register_blueprint(link) app.jinja_env.filters['from_json'] = from_json app.jinja_env.globals.update(get_working_context=get_working_context) Loading src/webui/service/main/routes.py +87 −18 Original line number Diff line number Diff line Loading @@ -11,19 +11,26 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import copy, json, logging from flask import jsonify, redirect, render_template, Blueprint, flash, session, url_for, request from common.proto.context_pb2 import Context, Device, Empty, Link, Service, Topology, ContextIdList from common.proto.context_pb2 import Connection, Context, Device, Empty, Link, Service, Slice, Topology, ContextIdList from common.tools.grpc.Tools import grpc_message_to_json_string from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from service.client.ServiceClient import ServiceClient from slice.client.SliceClient import SliceClient from webui.service.main.forms import ContextForm, DescriptorForm main = Blueprint('main', __name__) context_client = ContextClient() device_client = DeviceClient() service_client = ServiceClient() slice_client = SliceClient() logger = logging.getLogger(__name__) ENTITY_TO_TEXT = { # name => singular, plural 'context' : ('Context', 'Contexts' ), Loading @@ -31,12 +38,16 @@ ENTITY_TO_TEXT = { 'device' : ('Device', 'Devices' ), 'link' : ('Link', 'Links' ), 'service' : ('Service', 'Services' ), 'slice' : ('Slice', 'Slices' ), 'connection': ('Connection', 'Connections'), } ACTION_TO_TEXT = { # action => infinitive, past 'add' : ('Add', 'Added'), 'update' : ('Update', 'Updated'), } def process_descriptor(entity_name, action_name, grpc_method, grpc_class, entities): entity_name_singluar,entity_name_plural = ENTITY_TO_TEXT[entity_name] action_infinitive, action_past = ACTION_TO_TEXT[action_name] Loading @@ -50,25 +61,56 @@ def process_descriptor(entity_name, action_name, grpc_method, grpc_class, entiti num_err += 1 if num_ok : flash(f'{str(num_ok)} {entity_name_plural} {action_past}', 'success') if num_err: flash(f'{str(num_err)} {entity_name_plural} failed', 'danger') def process_descriptors(descriptors): logger.warning(str(descriptors.data)) logger.warning(str(descriptors.name)) try: logger.warning(str(request.files)) descriptors_file = request.files[descriptors.name] logger.warning(str(descriptors_file)) descriptors_data = descriptors_file.read() logger.warning(str(descriptors_data)) descriptors = json.loads(descriptors_data) logger.warning(str(descriptors)) except Exception as e: # pylint: disable=broad-except flash(f'Unable to load descriptor file: {str(e)}', 'danger') return dummy_mode = descriptors.get('dummy_mode' , False) contexts = descriptors.get('contexts' , []) topologies = descriptors.get('topologies' , []) devices = descriptors.get('devices' , []) links = descriptors.get('links' , []) services = descriptors.get('services' , []) slices = descriptors.get('slices' , []) connections = descriptors.get('connections', []) if dummy_mode: # Dummy Mode: used to pre-load databases (WebUI debugging purposes) with no smart or automated tasks. context_client.connect() contexts_add = copy.deepcopy(contexts) for context in contexts_add: context['topology_ids'] = [] context['service_ids'] = [] topologies_add = copy.deepcopy(topologies) for topology in topologies_add: topology['device_ids'] = [] topology['link_ids'] = [] process_descriptor('context', 'add', context_client.SetContext, Context, contexts_add ) process_descriptor('topology', 'add', context_client.SetTopology, Topology, topologies_add) process_descriptor('device', 'add', context_client.SetDevice, Device, devices ) process_descriptor('link', 'add', context_client.SetLink, Link, links ) process_descriptor('service', 'add', context_client.SetService, Service, services ) process_descriptor('context', 'update', context_client.SetContext, Context, contexts ) process_descriptor('topology', 'update', context_client.SetTopology, Topology, topologies ) process_descriptor('slice', 'add', context_client.SetSlice, Slice, slices ) process_descriptor('connection', 'add', context_client.SetConnection, Connection, connections ) context_client.close() return # Normal mode: follows the automated workflows in the different components # in normal mode, connections should not be set assert len(connections) == 0 services_add = [] for service in services: service_copy = copy.deepcopy(service) Loading @@ -76,18 +118,34 @@ def process_descriptors(descriptors): service_copy['service_constraints'] = [] service_copy['service_config'] = {'config_rules': []} services_add.append(service_copy) slices_add = [] for slice in slices: slice_copy = copy.deepcopy(slice) slice_copy['slice_endpoint_ids'] = [] slice_copy['slice_constraints'] = [] slice_copy['slice_config'] = {'config_rules': []} slices_add.append(slice_copy) context_client.connect() device_client.connect() service_client.connect() slice_client.connect() process_descriptor('context', 'add', context_client.SetContext, Context, contexts ) process_descriptor('topology', 'add', context_client.SetTopology, Topology, topologies ) process_descriptor('device', 'add', device_client .AddDevice, Device, devices ) process_descriptor('link', 'add', context_client.SetLink, Link, links ) process_descriptor('service', 'add', service_client.CreateService, Service, services_add) process_descriptor('service', 'update', service_client.UpdateService, Service, services ) process_descriptor('slice', 'add', slice_client.CreateSlice, Slice, slices_add ) process_descriptor('slice', 'update', slice_client.UpdateSlice, Slice, slices ) slice_client.close() service_client.close() device_client.close() context_client.close() @main.route('/', methods=['GET', 'POST']) def home(): context_client.connect() Loading @@ -95,14 +153,18 @@ def home(): response: ContextIdList = context_client.ListContextIds(Empty()) 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') return redirect(url_for("main.home")) if 'context_uuid' in session: context_form.context.data = session['context_uuid'] descriptor_form: DescriptorForm = DescriptorForm() try: if descriptor_form.validate_on_submit(): Loading @@ -114,7 +176,9 @@ def home(): finally: context_client.close() device_client.close() return render_template('main/home.html', context_form=context_form, descriptor_form=descriptor_form) @main.route('/topology', methods=['GET']) def topology(): context_client.connect() Loading @@ -125,6 +189,7 @@ def topology(): 'name': device.device_id.device_uuid.uuid, 'type': device.device_type, } for device in response.devices] response = context_client.ListLinks(Empty()) links = [] for link in response.links: Loading @@ -137,17 +202,21 @@ def topology(): 'source': link.link_endpoint_ids[0].device_id.device_uuid.uuid, 'target': link.link_endpoint_ids[1].device_id.device_uuid.uuid, }) return jsonify({'devices': devices, 'links': links}) except: logger.exception('Error retrieving topology') finally: context_client.close() @main.get('/about') def about(): return render_template('main/about.html') @main.get('/debug') def debug(): return render_template('main/debug.html') @main.get('/resetsession') def reset_session(): session.clear() Loading Loading
manifests/webuiservice.yaml +40 −40 Original line number Diff line number Diff line Loading @@ -60,43 +60,43 @@ spec: limits: cpu: 700m memory: 1024Mi - name: grafana image: grafana/grafana:8.2.6 imagePullPolicy: IfNotPresent ports: - containerPort: 3000 name: http-grafana protocol: TCP env: - name: GF_SERVER_ROOT_URL value: "http://0.0.0.0:3000/grafana/" - name: GF_SERVER_SERVE_FROM_SUB_PATH value: "true" readinessProbe: failureThreshold: 3 httpGet: path: /robots.txt port: 3000 scheme: HTTP initialDelaySeconds: 10 periodSeconds: 30 successThreshold: 1 timeoutSeconds: 2 livenessProbe: failureThreshold: 3 initialDelaySeconds: 30 periodSeconds: 10 successThreshold: 1 tcpSocket: port: 3000 timeoutSeconds: 1 resources: requests: cpu: 250m memory: 750Mi limits: cpu: 700m memory: 1024Mi # - name: grafana # image: grafana/grafana:8.2.6 # imagePullPolicy: IfNotPresent # ports: # - containerPort: 3000 # name: http-grafana # protocol: TCP # env: # - name: GF_SERVER_ROOT_URL # value: "http://0.0.0.0:3000/grafana/" # - name: GF_SERVER_SERVE_FROM_SUB_PATH # value: "true" # readinessProbe: # failureThreshold: 3 # httpGet: # path: /robots.txt # port: 3000 # scheme: HTTP # initialDelaySeconds: 10 # periodSeconds: 30 # successThreshold: 1 # timeoutSeconds: 2 # livenessProbe: # failureThreshold: 3 # initialDelaySeconds: 30 # periodSeconds: 10 # successThreshold: 1 # tcpSocket: # port: 3000 # timeoutSeconds: 1 # resources: # requests: # cpu: 250m # memory: 750Mi # limits: # cpu: 700m # memory: 1024Mi --- apiVersion: v1 kind: Service Loading @@ -110,6 +110,6 @@ spec: - name: webui port: 8004 targetPort: 8004 - name: grafana port: 3000 targetPort: 3000 # - name: grafana # port: 3000 # targetPort: 3000
my_deploy.sh +0 −7 Original line number Diff line number Diff line # Set the URL of your local Docker registry where the images will be uploaded to. export TFS_REGISTRY_IMAGE="http://localhost:32000/tfs/" # Set the list of components, separated by spaces, you want to build images for, and deploy. Loading @@ -11,12 +10,6 @@ export TFS_COMPONENTS="context device automation service compute monitoring webu # Set the tag you want to use for your images. export TFS_IMAGE_TAG="dev" # Set the name of the Kubernetes namespace to deploy to. export TFS_K8S_NAMESPACE="tfs" # Set additional manifest files to be applied after the deployment export TFS_EXTRA_MANIFESTS="manifests/nginx_ingress_http.yaml" # Set the neew Grafana admin password export TFS_GRAFANA_PASSWORD="admin123+"
src/webui/Dockerfile +2 −0 Original line number Diff line number Diff line Loading @@ -79,6 +79,8 @@ COPY --chown=webui:webui src/device/__init__.py device/__init__.py COPY --chown=webui:webui src/device/client/. device/client/ COPY --chown=webui:webui src/service/__init__.py service/__init__.py COPY --chown=webui:webui src/service/client/. service/client/ COPY --chown=webui:webui src/slice/__init__.py slice/__init__.py COPY --chown=webui:webui src/slice/client/. slice/client/ COPY --chown=webui:webui src/webui/. webui/ # Start the service Loading
src/webui/service/__init__.py +4 −0 Original line number Diff line number Diff line Loading @@ -72,12 +72,16 @@ def create_app(use_config=None, web_app_root=None): from webui.service.service.routes import service app.register_blueprint(service) from webui.service.slice.routes import slice app.register_blueprint(slice) from webui.service.device.routes import device app.register_blueprint(device) from webui.service.link.routes import link app.register_blueprint(link) app.jinja_env.filters['from_json'] = from_json app.jinja_env.globals.update(get_working_context=get_working_context) Loading
src/webui/service/main/routes.py +87 −18 Original line number Diff line number Diff line Loading @@ -11,19 +11,26 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import copy, json, logging from flask import jsonify, redirect, render_template, Blueprint, flash, session, url_for, request from common.proto.context_pb2 import Context, Device, Empty, Link, Service, Topology, ContextIdList from common.proto.context_pb2 import Connection, Context, Device, Empty, Link, Service, Slice, Topology, ContextIdList from common.tools.grpc.Tools import grpc_message_to_json_string from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient from service.client.ServiceClient import ServiceClient from slice.client.SliceClient import SliceClient from webui.service.main.forms import ContextForm, DescriptorForm main = Blueprint('main', __name__) context_client = ContextClient() device_client = DeviceClient() service_client = ServiceClient() slice_client = SliceClient() logger = logging.getLogger(__name__) ENTITY_TO_TEXT = { # name => singular, plural 'context' : ('Context', 'Contexts' ), Loading @@ -31,12 +38,16 @@ ENTITY_TO_TEXT = { 'device' : ('Device', 'Devices' ), 'link' : ('Link', 'Links' ), 'service' : ('Service', 'Services' ), 'slice' : ('Slice', 'Slices' ), 'connection': ('Connection', 'Connections'), } ACTION_TO_TEXT = { # action => infinitive, past 'add' : ('Add', 'Added'), 'update' : ('Update', 'Updated'), } def process_descriptor(entity_name, action_name, grpc_method, grpc_class, entities): entity_name_singluar,entity_name_plural = ENTITY_TO_TEXT[entity_name] action_infinitive, action_past = ACTION_TO_TEXT[action_name] Loading @@ -50,25 +61,56 @@ def process_descriptor(entity_name, action_name, grpc_method, grpc_class, entiti num_err += 1 if num_ok : flash(f'{str(num_ok)} {entity_name_plural} {action_past}', 'success') if num_err: flash(f'{str(num_err)} {entity_name_plural} failed', 'danger') def process_descriptors(descriptors): logger.warning(str(descriptors.data)) logger.warning(str(descriptors.name)) try: logger.warning(str(request.files)) descriptors_file = request.files[descriptors.name] logger.warning(str(descriptors_file)) descriptors_data = descriptors_file.read() logger.warning(str(descriptors_data)) descriptors = json.loads(descriptors_data) logger.warning(str(descriptors)) except Exception as e: # pylint: disable=broad-except flash(f'Unable to load descriptor file: {str(e)}', 'danger') return dummy_mode = descriptors.get('dummy_mode' , False) contexts = descriptors.get('contexts' , []) topologies = descriptors.get('topologies' , []) devices = descriptors.get('devices' , []) links = descriptors.get('links' , []) services = descriptors.get('services' , []) slices = descriptors.get('slices' , []) connections = descriptors.get('connections', []) if dummy_mode: # Dummy Mode: used to pre-load databases (WebUI debugging purposes) with no smart or automated tasks. context_client.connect() contexts_add = copy.deepcopy(contexts) for context in contexts_add: context['topology_ids'] = [] context['service_ids'] = [] topologies_add = copy.deepcopy(topologies) for topology in topologies_add: topology['device_ids'] = [] topology['link_ids'] = [] process_descriptor('context', 'add', context_client.SetContext, Context, contexts_add ) process_descriptor('topology', 'add', context_client.SetTopology, Topology, topologies_add) process_descriptor('device', 'add', context_client.SetDevice, Device, devices ) process_descriptor('link', 'add', context_client.SetLink, Link, links ) process_descriptor('service', 'add', context_client.SetService, Service, services ) process_descriptor('context', 'update', context_client.SetContext, Context, contexts ) process_descriptor('topology', 'update', context_client.SetTopology, Topology, topologies ) process_descriptor('slice', 'add', context_client.SetSlice, Slice, slices ) process_descriptor('connection', 'add', context_client.SetConnection, Connection, connections ) context_client.close() return # Normal mode: follows the automated workflows in the different components # in normal mode, connections should not be set assert len(connections) == 0 services_add = [] for service in services: service_copy = copy.deepcopy(service) Loading @@ -76,18 +118,34 @@ def process_descriptors(descriptors): service_copy['service_constraints'] = [] service_copy['service_config'] = {'config_rules': []} services_add.append(service_copy) slices_add = [] for slice in slices: slice_copy = copy.deepcopy(slice) slice_copy['slice_endpoint_ids'] = [] slice_copy['slice_constraints'] = [] slice_copy['slice_config'] = {'config_rules': []} slices_add.append(slice_copy) context_client.connect() device_client.connect() service_client.connect() slice_client.connect() process_descriptor('context', 'add', context_client.SetContext, Context, contexts ) process_descriptor('topology', 'add', context_client.SetTopology, Topology, topologies ) process_descriptor('device', 'add', device_client .AddDevice, Device, devices ) process_descriptor('link', 'add', context_client.SetLink, Link, links ) process_descriptor('service', 'add', service_client.CreateService, Service, services_add) process_descriptor('service', 'update', service_client.UpdateService, Service, services ) process_descriptor('slice', 'add', slice_client.CreateSlice, Slice, slices_add ) process_descriptor('slice', 'update', slice_client.UpdateSlice, Slice, slices ) slice_client.close() service_client.close() device_client.close() context_client.close() @main.route('/', methods=['GET', 'POST']) def home(): context_client.connect() Loading @@ -95,14 +153,18 @@ def home(): response: ContextIdList = context_client.ListContextIds(Empty()) 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') return redirect(url_for("main.home")) if 'context_uuid' in session: context_form.context.data = session['context_uuid'] descriptor_form: DescriptorForm = DescriptorForm() try: if descriptor_form.validate_on_submit(): Loading @@ -114,7 +176,9 @@ def home(): finally: context_client.close() device_client.close() return render_template('main/home.html', context_form=context_form, descriptor_form=descriptor_form) @main.route('/topology', methods=['GET']) def topology(): context_client.connect() Loading @@ -125,6 +189,7 @@ def topology(): 'name': device.device_id.device_uuid.uuid, 'type': device.device_type, } for device in response.devices] response = context_client.ListLinks(Empty()) links = [] for link in response.links: Loading @@ -137,17 +202,21 @@ def topology(): 'source': link.link_endpoint_ids[0].device_id.device_uuid.uuid, 'target': link.link_endpoint_ids[1].device_id.device_uuid.uuid, }) return jsonify({'devices': devices, 'links': links}) except: logger.exception('Error retrieving topology') finally: context_client.close() @main.get('/about') def about(): return render_template('main/about.html') @main.get('/debug') def debug(): return render_template('main/debug.html') @main.get('/resetsession') def reset_session(): session.clear() Loading