Loading src/webui/service/link/routes.py +14 −3 Original line number Diff line number Diff line Loading @@ -12,10 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. from flask import render_template, Blueprint, flash, session, redirect, url_for from common.proto.context_pb2 import Empty, LinkList from flask import current_app, render_template, Blueprint, flash, session, redirect, url_for from common.proto.context_pb2 import Empty, Link, LinkEvent, LinkId, LinkIdList, LinkList, DeviceId from context.client.ContextClient import ContextClient link = Blueprint('link', __name__, url_prefix='/link') context_client = ContextClient() Loading @@ -33,3 +35,12 @@ def home(): "link/home.html", links=response.links, ) @link.route('detail/<path:link_uuid>', methods=('GET', 'POST')) def detail(link_uuid: str): request = LinkId() request.link_uuid.uuid = link_uuid context_client.connect() response = context_client.GetLink(request) context_client.close() return render_template('link/detail.html',link=response) src/webui/service/main/routes.py +58 −32 Original line number Diff line number Diff line Loading @@ -11,33 +11,45 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import json, logging 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, Topology, ContextIdList from common.proto.context_pb2 import Context, Device, Empty, Link, Service, 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 webui.service.main.forms import ContextForm, DescriptorForm main = Blueprint('main', __name__) context_client = ContextClient() device_client = DeviceClient() service_client = ServiceClient() logger = logging.getLogger(__name__) def process_descriptor(item_name_singluar, item_name_plural, grpc_method, grpc_class, items): ENTITY_TO_TEXT = { # name => singular, plural 'context' : ('Context', 'Contexts' ), 'topology': ('Topology', 'Topologies'), 'device' : ('Device', 'Devices' ), 'link' : ('Link', 'Links' ), 'service' : ('Service', 'Services' ), } 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] num_ok, num_err = 0, 0 for item in items: for entity in entities: try: grpc_method(grpc_class(**item)) grpc_method(grpc_class(**entity)) num_ok += 1 except Exception as e: # pylint: disable=broad-except flash(f'Unable to add {item_name_singluar} {str(item)}: {str(e)}', 'error') flash(f'Unable to {action_infinitive} {entity_name_singluar} {str(entity)}: {str(e)}', 'error') num_err += 1 if num_ok : flash(f'{str(num_ok)} {item_name_plural} added', 'success') if num_err: flash(f'{str(num_err)} {item_name_plural} failed', 'danger') 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)) Loading @@ -52,16 +64,30 @@ def process_descriptors(descriptors): except Exception as e: # pylint: disable=broad-except flash(f'Unable to load descriptor file: {str(e)}', 'danger') return contexts = descriptors.get('contexts' , []) topologies = descriptors.get('topologies', []) devices = descriptors.get('devices' , []) links = descriptors.get('links' , []) services = descriptors.get('services' , []) services_add = [] for service in services: service_copy = copy.deepcopy(service) service_copy['service_endpoint_ids'] = [] service_copy['service_constraints'] = [] service_copy['service_config'] = {'config_rules': []} services_add.append(service_copy) context_client.connect() device_client.connect() process_descriptor('Context', 'Contexts', context_client.SetContext, Context, descriptors['contexts' ]) process_descriptor('Topology', 'Topologies', context_client.SetTopology, Topology, descriptors['topologies']) process_descriptor('Device', 'Devices', device_client .AddDevice, Device, descriptors['devices' ]) process_descriptor('Link', 'Links', context_client.SetLink, Link, descriptors['links' ]) service_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 ) service_client.close() device_client.close() context_client.close() @main.route('/', methods=['GET', 'POST']) def home(): context_client.connect() Loading Loading @@ -89,7 +115,6 @@ def home(): 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 @@ -100,28 +125,29 @@ def topology(): 'name': device.device_id.device_uuid.uuid, 'type': device.device_type, } for device in response.devices] response = context_client.ListLinks(Empty()) links = [{ links = [] for link in response.links: if len(link.link_endpoint_ids) != 2: str_link = grpc_message_to_json_string(link) logger.warning('Unexpected link with len(endpoints) != 2: {:s}'.format(str_link)) continue links.append({ 'id': link.link_id.link_uuid.uuid, 'source': link.link_endpoint_ids[0].device_id.device_uuid.uuid, 'target': link.link_endpoint_ids[1].device_id.device_uuid.uuid, } for link in response.links] }) 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 src/webui/service/service/routes.py +4 −3 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ import grpc from flask import current_app, redirect, render_template, Blueprint, flash, session, url_for from common.proto.context_pb2 import ContextId, Service, ServiceId, ServiceList, ServiceTypeEnum, ServiceStatusEnum from common.proto.context_pb2 import ContextId, Service, ServiceId, ServiceList, ServiceTypeEnum, ServiceStatusEnum, Connection from context.client.ContextClient import ContextClient from service.client.ServiceClient import ServiceClient Loading Loading @@ -73,12 +73,13 @@ def detail(service_uuid: str): try: context_client.connect() response: Service = context_client.GetService(request) connections: Connection = context_client.ListConnections(request) context_client.close() except Exception as e: flash('The system encountered an error and cannot show the details of this service.', 'warning') current_app.logger.exception(e) return redirect(url_for('service.home')) return render_template('service/detail.html', service=response) return render_template('service/detail.html', service=response, connections=connections) @service.get('<path:service_uuid>/delete') Loading src/webui/service/templates/device/detail.html +120 −102 Original line number Diff line number Diff line Loading @@ -40,54 +40,71 @@ </div> </div> <br> <div class="row mb-3"> <div class="col-sm-1"><b>UUID:</b></div> <div class="col-sm-5"> {{ device.device_id.device_uuid.uuid }} </div> <div class="col-sm-1"><b>Type:</b></div> <div class="col-sm-5"> {{ device.device_type }} </div> </div> <div class="row mb-3"> <div class="col-sm-1"><b>Drivers:</b></div> <div class="col-sm-11"> <div class="col-sm-4"> <b>UUID: </b>{{ device.device_id.device_uuid.uuid }}<br><br> <b>Type: </b>{{ device.device_type }}<br><br> <b>Drivers: </b> <ul> {% for driver in device.device_drivers %} <li>{{ dde.Name(driver).replace('DEVICEDRIVER_', '').replace('UNDEFINED', 'EMULATED') }}</li> {% endfor %} </ul> </div> </div> <div class="row mb-3"> <b>Endpoints:</b> <div class="col-sm-10"> <ul> <div class="col-sm-8"> <table class="table table-striped table-hover"> <thead> <tr> <th scope="col">Endpoints</th> <th scope="col">Type</th> </tr> </thead> <tbody> {% for endpoint in device.device_endpoints %} <li>{{ endpoint.endpoint_id.endpoint_uuid.uuid }}: {{ endpoint.endpoint_type }}</li> <tr> <td> {{ endpoint.endpoint_id.endpoint_uuid.uuid }} </td> <td> {{ endpoint.endpoint_type }} </td> </tr> {% endfor %} </ul> </tbody> </table> </div> </div> <div class="row mb-3"> </div> <b>Configurations:</b> <div class="col-sm-10"> <ul> <table class="table table-striped table-hover"> <thead> <tr> <th scope="col">Key</th> <th scope="col">Value</th> </tr> </thead> <tbody> {% for config in device.device_config.config_rules %} {% if config.WhichOneof('config_rule') == 'custom' %} <li>{{ config.custom.resource_key }}: <tr> <td> {{ config.custom.resource_key }} </td> <td> <ul> {% for key, value in (config.custom.resource_value | from_json).items() %} <li><b>{{ key }}:</b> {{ value }}</li> {% endfor %} </ul> </li> </td> </tr> {% endif %} {% endfor %} </ul> </div> </div> </tbody> </table> <!-- Modal --> <div class="modal fade" id="deleteModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true"> Loading @@ -109,3 +126,4 @@ </div> {% endblock %} No newline at end of file src/webui/service/templates/link/detail.html 0 → 100644 +59 −0 Original line number Diff line number Diff line <!-- Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> {% extends 'base.html' %} {% block content %} <h1>Link {{ link.link_id.link_uuid.uuid }}</h1> <div class="row mb-3"> <div class="col-sm-3"> <button type="button" class="btn btn-success" onclick="window.location.href='{{ url_for('link.home') }}'"> <i class="bi bi-box-arrow-in-left"></i> Back to link list </button> </div> </div> <br> <div class="row mb-3"> <div class="col-sm-4"> <b>UUID: </b>{{ link.link_id.link_uuid.uuid }}<br><br> </div> <div class="col-sm-8"> <table class="table table-striped table-hover"> <thead> <tr> <th scope="col">Endpoints</th> <th scope="col">Type</th> </tr> </thead> <tbody> {% for end_point in link.link_endpoint_ids %} <tr> <td> {{ end_point.endpoint_uuid.uuid }} </td> <td> {{ end_point.endpoint_uuid.uuid }} </td> </tr> {% endfor %} </tbody> </table> </div> </div> {% endblock %} No newline at end of file Loading
src/webui/service/link/routes.py +14 −3 Original line number Diff line number Diff line Loading @@ -12,10 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. from flask import render_template, Blueprint, flash, session, redirect, url_for from common.proto.context_pb2 import Empty, LinkList from flask import current_app, render_template, Blueprint, flash, session, redirect, url_for from common.proto.context_pb2 import Empty, Link, LinkEvent, LinkId, LinkIdList, LinkList, DeviceId from context.client.ContextClient import ContextClient link = Blueprint('link', __name__, url_prefix='/link') context_client = ContextClient() Loading @@ -33,3 +35,12 @@ def home(): "link/home.html", links=response.links, ) @link.route('detail/<path:link_uuid>', methods=('GET', 'POST')) def detail(link_uuid: str): request = LinkId() request.link_uuid.uuid = link_uuid context_client.connect() response = context_client.GetLink(request) context_client.close() return render_template('link/detail.html',link=response)
src/webui/service/main/routes.py +58 −32 Original line number Diff line number Diff line Loading @@ -11,33 +11,45 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import json, logging 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, Topology, ContextIdList from common.proto.context_pb2 import Context, Device, Empty, Link, Service, 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 webui.service.main.forms import ContextForm, DescriptorForm main = Blueprint('main', __name__) context_client = ContextClient() device_client = DeviceClient() service_client = ServiceClient() logger = logging.getLogger(__name__) def process_descriptor(item_name_singluar, item_name_plural, grpc_method, grpc_class, items): ENTITY_TO_TEXT = { # name => singular, plural 'context' : ('Context', 'Contexts' ), 'topology': ('Topology', 'Topologies'), 'device' : ('Device', 'Devices' ), 'link' : ('Link', 'Links' ), 'service' : ('Service', 'Services' ), } 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] num_ok, num_err = 0, 0 for item in items: for entity in entities: try: grpc_method(grpc_class(**item)) grpc_method(grpc_class(**entity)) num_ok += 1 except Exception as e: # pylint: disable=broad-except flash(f'Unable to add {item_name_singluar} {str(item)}: {str(e)}', 'error') flash(f'Unable to {action_infinitive} {entity_name_singluar} {str(entity)}: {str(e)}', 'error') num_err += 1 if num_ok : flash(f'{str(num_ok)} {item_name_plural} added', 'success') if num_err: flash(f'{str(num_err)} {item_name_plural} failed', 'danger') 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)) Loading @@ -52,16 +64,30 @@ def process_descriptors(descriptors): except Exception as e: # pylint: disable=broad-except flash(f'Unable to load descriptor file: {str(e)}', 'danger') return contexts = descriptors.get('contexts' , []) topologies = descriptors.get('topologies', []) devices = descriptors.get('devices' , []) links = descriptors.get('links' , []) services = descriptors.get('services' , []) services_add = [] for service in services: service_copy = copy.deepcopy(service) service_copy['service_endpoint_ids'] = [] service_copy['service_constraints'] = [] service_copy['service_config'] = {'config_rules': []} services_add.append(service_copy) context_client.connect() device_client.connect() process_descriptor('Context', 'Contexts', context_client.SetContext, Context, descriptors['contexts' ]) process_descriptor('Topology', 'Topologies', context_client.SetTopology, Topology, descriptors['topologies']) process_descriptor('Device', 'Devices', device_client .AddDevice, Device, descriptors['devices' ]) process_descriptor('Link', 'Links', context_client.SetLink, Link, descriptors['links' ]) service_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 ) service_client.close() device_client.close() context_client.close() @main.route('/', methods=['GET', 'POST']) def home(): context_client.connect() Loading Loading @@ -89,7 +115,6 @@ def home(): 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 @@ -100,28 +125,29 @@ def topology(): 'name': device.device_id.device_uuid.uuid, 'type': device.device_type, } for device in response.devices] response = context_client.ListLinks(Empty()) links = [{ links = [] for link in response.links: if len(link.link_endpoint_ids) != 2: str_link = grpc_message_to_json_string(link) logger.warning('Unexpected link with len(endpoints) != 2: {:s}'.format(str_link)) continue links.append({ 'id': link.link_id.link_uuid.uuid, 'source': link.link_endpoint_ids[0].device_id.device_uuid.uuid, 'target': link.link_endpoint_ids[1].device_id.device_uuid.uuid, } for link in response.links] }) 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
src/webui/service/service/routes.py +4 −3 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ import grpc from flask import current_app, redirect, render_template, Blueprint, flash, session, url_for from common.proto.context_pb2 import ContextId, Service, ServiceId, ServiceList, ServiceTypeEnum, ServiceStatusEnum from common.proto.context_pb2 import ContextId, Service, ServiceId, ServiceList, ServiceTypeEnum, ServiceStatusEnum, Connection from context.client.ContextClient import ContextClient from service.client.ServiceClient import ServiceClient Loading Loading @@ -73,12 +73,13 @@ def detail(service_uuid: str): try: context_client.connect() response: Service = context_client.GetService(request) connections: Connection = context_client.ListConnections(request) context_client.close() except Exception as e: flash('The system encountered an error and cannot show the details of this service.', 'warning') current_app.logger.exception(e) return redirect(url_for('service.home')) return render_template('service/detail.html', service=response) return render_template('service/detail.html', service=response, connections=connections) @service.get('<path:service_uuid>/delete') Loading
src/webui/service/templates/device/detail.html +120 −102 Original line number Diff line number Diff line Loading @@ -40,54 +40,71 @@ </div> </div> <br> <div class="row mb-3"> <div class="col-sm-1"><b>UUID:</b></div> <div class="col-sm-5"> {{ device.device_id.device_uuid.uuid }} </div> <div class="col-sm-1"><b>Type:</b></div> <div class="col-sm-5"> {{ device.device_type }} </div> </div> <div class="row mb-3"> <div class="col-sm-1"><b>Drivers:</b></div> <div class="col-sm-11"> <div class="col-sm-4"> <b>UUID: </b>{{ device.device_id.device_uuid.uuid }}<br><br> <b>Type: </b>{{ device.device_type }}<br><br> <b>Drivers: </b> <ul> {% for driver in device.device_drivers %} <li>{{ dde.Name(driver).replace('DEVICEDRIVER_', '').replace('UNDEFINED', 'EMULATED') }}</li> {% endfor %} </ul> </div> </div> <div class="row mb-3"> <b>Endpoints:</b> <div class="col-sm-10"> <ul> <div class="col-sm-8"> <table class="table table-striped table-hover"> <thead> <tr> <th scope="col">Endpoints</th> <th scope="col">Type</th> </tr> </thead> <tbody> {% for endpoint in device.device_endpoints %} <li>{{ endpoint.endpoint_id.endpoint_uuid.uuid }}: {{ endpoint.endpoint_type }}</li> <tr> <td> {{ endpoint.endpoint_id.endpoint_uuid.uuid }} </td> <td> {{ endpoint.endpoint_type }} </td> </tr> {% endfor %} </ul> </tbody> </table> </div> </div> <div class="row mb-3"> </div> <b>Configurations:</b> <div class="col-sm-10"> <ul> <table class="table table-striped table-hover"> <thead> <tr> <th scope="col">Key</th> <th scope="col">Value</th> </tr> </thead> <tbody> {% for config in device.device_config.config_rules %} {% if config.WhichOneof('config_rule') == 'custom' %} <li>{{ config.custom.resource_key }}: <tr> <td> {{ config.custom.resource_key }} </td> <td> <ul> {% for key, value in (config.custom.resource_value | from_json).items() %} <li><b>{{ key }}:</b> {{ value }}</li> {% endfor %} </ul> </li> </td> </tr> {% endif %} {% endfor %} </ul> </div> </div> </tbody> </table> <!-- Modal --> <div class="modal fade" id="deleteModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true"> Loading @@ -109,3 +126,4 @@ </div> {% endblock %} No newline at end of file
src/webui/service/templates/link/detail.html 0 → 100644 +59 −0 Original line number Diff line number Diff line <!-- Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> {% extends 'base.html' %} {% block content %} <h1>Link {{ link.link_id.link_uuid.uuid }}</h1> <div class="row mb-3"> <div class="col-sm-3"> <button type="button" class="btn btn-success" onclick="window.location.href='{{ url_for('link.home') }}'"> <i class="bi bi-box-arrow-in-left"></i> Back to link list </button> </div> </div> <br> <div class="row mb-3"> <div class="col-sm-4"> <b>UUID: </b>{{ link.link_id.link_uuid.uuid }}<br><br> </div> <div class="col-sm-8"> <table class="table table-striped table-hover"> <thead> <tr> <th scope="col">Endpoints</th> <th scope="col">Type</th> </tr> </thead> <tbody> {% for end_point in link.link_endpoint_ids %} <tr> <td> {{ end_point.endpoint_uuid.uuid }} </td> <td> {{ end_point.endpoint_uuid.uuid }} </td> </tr> {% endfor %} </tbody> </table> </div> </div> {% endblock %} No newline at end of file