diff --git a/src/webui/service/__init__.py b/src/webui/service/__init__.py index d5b40b486dd7772cea29fd7d71db949b2954155c..ef5253b876085c152fa7a71ffb5a29cfd1f90516 100644 --- a/src/webui/service/__init__.py +++ b/src/webui/service/__init__.py @@ -96,6 +96,7 @@ def create_app(use_config=None, web_app_root=None): app.register_blueprint(link) app.jinja_env.globals.update({ # pylint: disable=no-member + 'enumerate' : enumerate, 'json_to_list' : json_to_list, 'get_working_context' : get_working_context, 'get_working_topology': get_working_topology, diff --git a/src/webui/service/device/forms.py b/src/webui/service/device/forms.py index e496c4d432c7c9d02227141ea6d618984378c185..c6bacac9bc1723a020f3057fad9c9e8306c9dbca 100644 --- a/src/webui/service/device/forms.py +++ b/src/webui/service/device/forms.py @@ -12,21 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -# external imports from flask_wtf import FlaskForm -from wtforms import StringField, SelectField, TextAreaField, SubmitField, BooleanField, Form -from wtforms.validators import DataRequired, Length, NumberRange, Regexp, ValidationError +from wtforms import StringField, SelectField, TextAreaField, SubmitField, BooleanField +from wtforms.validators import DataRequired, Length, NumberRange, ValidationError from common.proto.context_pb2 import DeviceOperationalStatusEnum -from webui.utils.form_validators import key_value_validator class AddDeviceForm(FlaskForm): device_id = StringField('ID', validators=[DataRequired(), Length(min=5)]) - device_type = SelectField('Type', choices = []) - operational_status = SelectField('Operational Status', - # choices=[(-1, 'Select...'), (0, 'Undefined'), (1, 'Disabled'), (2, 'Enabled')], - coerce=int, - validators=[NumberRange(min=0)]) + device_type = SelectField('Type') + operational_status = SelectField('Operational Status', coerce=int, validators=[NumberRange(min=0)]) device_drivers_undefined = BooleanField('UNDEFINED / EMULATED') device_drivers_openconfig = BooleanField('OPENCONFIG') device_drivers_transport_api = BooleanField('TRANSPORT_API') diff --git a/src/webui/service/device/routes.py b/src/webui/service/device/routes.py index ce3edcfda45859c3e5db83c62fd328ee546762a5..ebf77a35ffdf9c2546ddbdd1bac0c8c1f54a2b56 100644 --- a/src/webui/service/device/routes.py +++ b/src/webui/service/device/routes.py @@ -14,16 +14,14 @@ import json from flask import current_app, render_template, Blueprint, flash, session, redirect, url_for +from common.DeviceTypes import DeviceTypeEnum from common.proto.context_pb2 import ( - ConfigActionEnum, Device, DeviceDriverEnum, DeviceId, DeviceList, DeviceOperationalStatusEnum, Empty, TopologyId) -from common.tools.object_factory.Context import json_context_id -from common.tools.object_factory.Topology import json_topology_id + ConfigActionEnum, Device, DeviceDriverEnum, DeviceId, DeviceList, DeviceOperationalStatusEnum, Empty) +from common.tools.context_queries.Device import get_device +from common.tools.context_queries.Topology import get_topology from context.client.ContextClient import ContextClient from device.client.DeviceClient import DeviceClient -from webui.service.device.forms import AddDeviceForm -from common.DeviceTypes import DeviceTypeEnum -from webui.service.device.forms import ConfigForm -from webui.service.device.forms import UpdateDeviceForm +from webui.service.device.forms import AddDeviceForm, ConfigForm, UpdateDeviceForm device = Blueprint('device', __name__, url_prefix='/device') context_client = ContextClient() @@ -39,17 +37,19 @@ def home(): topology_uuid = session['topology_uuid'] context_client.connect() - json_topo_id = json_topology_id(topology_uuid, context_id=json_context_id(context_uuid)) - grpc_topology = context_client.GetTopology(TopologyId(**json_topo_id)) - topo_device_uuids = {device_id.device_uuid.uuid for device_id in grpc_topology.device_ids} - grpc_devices: DeviceList = context_client.ListDevices(Empty()) + grpc_topology = get_topology(context_client, topology_uuid, context_uuid=context_uuid, rw_copy=False) + if grpc_topology is None: + flash('Context({:s})/Topology({:s}) not found'.format(str(context_uuid), str(topology_uuid)), 'danger') + devices = [] + else: + topo_device_uuids = {device_id.device_uuid.uuid for device_id in grpc_topology.device_ids} + grpc_devices: DeviceList = context_client.ListDevices(Empty()) + devices = [ + device for device in grpc_devices.devices + if device.device_id.device_uuid.uuid in topo_device_uuids + ] context_client.close() - devices = [ - device for device in grpc_devices.devices - if device.device_id.device_uuid.uuid in topo_device_uuids - ] - return render_template( 'device/home.html', devices=devices, dde=DeviceDriverEnum, dose=DeviceOperationalStatusEnum) @@ -71,23 +71,23 @@ def add(): if form.validate_on_submit(): device_obj = Device() # Device UUID: - device_obj.device_id.device_uuid.uuid = form.device_id.data + device_obj.device_id.device_uuid.uuid = form.device_id.data # pylint: disable=no-member # Device type: device_obj.device_type = str(form.device_type.data) # Device configurations: - config_rule = device_obj.device_config.config_rules.add() + config_rule = device_obj.device_config.config_rules.add() # pylint: disable=no-member config_rule.action = ConfigActionEnum.CONFIGACTION_SET config_rule.custom.resource_key = '_connect/address' config_rule.custom.resource_value = form.device_config_address.data - config_rule = device_obj.device_config.config_rules.add() + config_rule = device_obj.device_config.config_rules.add() # pylint: disable=no-member config_rule.action = ConfigActionEnum.CONFIGACTION_SET config_rule.custom.resource_key = '_connect/port' config_rule.custom.resource_value = form.device_config_port.data - config_rule = device_obj.device_config.config_rules.add() + config_rule = device_obj.device_config.config_rules.add() # pylint: disable=no-member config_rule.action = ConfigActionEnum.CONFIGACTION_SET config_rule.custom.resource_key = '_connect/settings' @@ -105,20 +105,22 @@ def add(): device_obj.device_operational_status = form.operational_status.data # Device drivers: + device_drivers = list() if form.device_drivers_undefined.data: - device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_UNDEFINED) + device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_UNDEFINED) if form.device_drivers_openconfig.data: - device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG) + device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG) if form.device_drivers_transport_api.data: - device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API) + device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API) if form.device_drivers_p4.data: - device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_P4) + device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_P4) if form.device_drivers_ietf_network_topology.data: - device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY) + device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY) if form.device_drivers_onf_tr_352.data: - device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352) + device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352) if form.device_drivers_xr.data: - device_obj.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_XR) + device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_XR) + device_obj.device_drivers.extend(device_drivers) # pylint: disable=no-member try: device_client.connect() @@ -126,7 +128,7 @@ def add(): device_client.close() flash(f'New device was created with ID "{response.device_uuid.uuid}".', 'success') return redirect(url_for('device.home')) - except Exception as e: + except Exception as e: # pylint: disable=broad-except flash(f'Problem adding the device. {e.details()}', 'danger') return render_template('device/add.html', form=form, @@ -134,14 +136,15 @@ def add(): @device.route('detail/<path:device_uuid>', methods=['GET', 'POST']) def detail(device_uuid: str): - request = DeviceId() - request.device_uuid.uuid = device_uuid context_client.connect() - response = context_client.GetDevice(request) + device_obj = get_device(context_client, device_uuid, rw_copy=False) + if device_obj is None: + flash('Device({:s}) not found'.format(str(device_uuid)), 'danger') + device_obj = Device() context_client.close() - return render_template('device/detail.html', device=response, - dde=DeviceDriverEnum, - dose=DeviceOperationalStatusEnum) + + return render_template( + 'device/detail.html', device=device_obj, dde=DeviceDriverEnum, dose=DeviceOperationalStatusEnum) @device.get('<path:device_uuid>/delete') def delete(device_uuid): @@ -154,13 +157,13 @@ def delete(device_uuid): # TODO: finalize implementation request = DeviceId() - request.device_uuid.uuid = device_uuid + request.device_uuid.uuid = device_uuid # pylint: disable=no-member device_client.connect() - response = device_client.DeleteDevice(request) + device_client.DeleteDevice(request) device_client.close() flash(f'Device "{device_uuid}" deleted successfully!', 'success') - except Exception as e: + except Exception as e: # pylint: disable=broad-except flash(f'Problem deleting device "{device_uuid}": {e.details()}', 'danger') current_app.logger.exception(e) return redirect(url_for('device.home')) @@ -169,25 +172,25 @@ def delete(device_uuid): def addconfig(device_uuid): form = ConfigForm() request = DeviceId() - request.device_uuid.uuid = device_uuid + request.device_uuid.uuid = device_uuid # pylint: disable=no-member context_client.connect() response = context_client.GetDevice(request) context_client.close() if form.validate_on_submit(): - device = Device() - device.CopyFrom(response) - config_rule = device.device_config.config_rules.add() + device_obj = Device() + device_obj.CopyFrom(response) + config_rule = device_obj.device_config.config_rules.add() # pylint: disable=no-member config_rule.action = ConfigActionEnum.CONFIGACTION_SET config_rule.custom.resource_key = form.device_key_config.data config_rule.custom.resource_value = form.device_value_config.data try: device_client.connect() - response: DeviceId = device_client.ConfigureDevice(device) + response: DeviceId = device_client.ConfigureDevice(device_obj) device_client.close() flash(f'New configuration was created with ID "{response.device_uuid.uuid}".', 'success') return redirect(url_for('device.home')) - except Exception as e: + except Exception as e: # pylint: disable=broad-except flash(f'Problem adding the device. {e.details()}', 'danger') return render_template('device/addconfig.html', form=form, submit_text='Add New Configuration') @@ -203,28 +206,29 @@ def updateconfig(): def update(device_uuid): form = UpdateDeviceForm() request = DeviceId() - request.device_uuid.uuid = device_uuid + request.device_uuid.uuid = device_uuid # pylint: disable=no-member context_client.connect() response = context_client.GetDevice(request) context_client.close() # listing enum values form.update_operational_status.choices = [] - for key, value in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_name.items(): - form.update_operational_status.choices.append((DeviceOperationalStatusEnum.Value(key), key.replace('DEVICEOPERATIONALSTATUS_', ''))) + for key, _ in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_name.items(): + item = (DeviceOperationalStatusEnum.Value(key), key.replace('DEVICEOPERATIONALSTATUS_', '')) + form.update_operational_status.choices.append(item) form.update_operational_status.default = response.device_operational_status if form.validate_on_submit(): - device = Device() - device.CopyFrom(response) - device.device_operational_status = form.update_operational_status.data + device_obj = Device() + device_obj.CopyFrom(response) + device_obj.device_operational_status = form.update_operational_status.data try: device_client.connect() - response: DeviceId = device_client.ConfigureDevice(device) + response: DeviceId = device_client.ConfigureDevice(device_obj) device_client.close() flash(f'Status of device with ID "{response.device_uuid.uuid}" was updated.', 'success') return redirect(url_for('device.home')) - except Exception as e: + except Exception as e: # pylint: disable=broad-except flash(f'Problem updating the device. {e.details()}', 'danger') return render_template('device/update.html', device=response, form=form, submit_text='Update Device') diff --git a/src/webui/service/link/routes.py b/src/webui/service/link/routes.py index 9324ad0be6d9e72dfd3413863f0590f6ec595c3b..0fda8958e2ab2609969d2c1f68aaae61b7360b68 100644 --- a/src/webui/service/link/routes.py +++ b/src/webui/service/link/routes.py @@ -14,10 +14,10 @@ from flask import render_template, Blueprint, flash, session, redirect, url_for -from common.proto.context_pb2 import Empty, LinkId, LinkList, TopologyId +from common.proto.context_pb2 import Empty, Link, LinkList from common.tools.context_queries.EndPoint import get_endpoint_names -from common.tools.object_factory.Context import json_context_id -from common.tools.object_factory.Topology import json_topology_id +from common.tools.context_queries.Link import get_link +from common.tools.context_queries.Topology import get_topology from context.client.ContextClient import ContextClient @@ -33,20 +33,21 @@ def home(): context_uuid = session['context_uuid'] topology_uuid = session['topology_uuid'] + links, endpoint_ids = list(), list() + device_names, endpoints_data = dict(), dict() + context_client.connect() - json_topo_id = json_topology_id(topology_uuid, context_id=json_context_id(context_uuid)) - grpc_topology = context_client.GetTopology(TopologyId(**json_topo_id)) - topo_link_uuids = {link_id.link_uuid.uuid for link_id in grpc_topology.link_ids} - grpc_links: LinkList = context_client.ListLinks(Empty()) - - endpoint_ids = [] - links = [] - for link_ in grpc_links.links: - if link_.link_id.link_uuid.uuid not in topo_link_uuids: continue - links.append(link_) - endpoint_ids.extend(link_.link_endpoint_ids) - - device_names, endpoints_data = get_endpoint_names(context_client, endpoint_ids) + grpc_topology = get_topology(context_client, topology_uuid, context_uuid=context_uuid, rw_copy=False) + if grpc_topology is None: + flash('Context({:s})/Topology({:s}) not found'.format(str(context_uuid), str(topology_uuid)), 'danger') + else: + topo_link_uuids = {link_id.link_uuid.uuid for link_id in grpc_topology.link_ids} + grpc_links: LinkList = context_client.ListLinks(Empty()) + for link_ in grpc_links.links: + if link_.link_id.link_uuid.uuid not in topo_link_uuids: continue + links.append(link_) + endpoint_ids.extend(link_.link_endpoint_ids) + device_names, endpoints_data = get_endpoint_names(context_client, endpoint_ids) context_client.close() return render_template('link/home.html', links=links, device_names=device_names, endpoints_data=endpoints_data) @@ -54,10 +55,13 @@ def home(): @link.route('detail/<path:link_uuid>', methods=('GET', 'POST')) def detail(link_uuid: str): - request = LinkId() - request.link_uuid.uuid = link_uuid # pylint: disable=no-member context_client.connect() - response = context_client.GetLink(request) - device_names, endpoints_data = get_endpoint_names(context_client, response.link_endpoint_ids) + link_obj = get_link(context_client, link_uuid, rw_copy=False) + if link_obj is None: + flash('Link({:s}) not found'.format(str(link_uuid)), 'danger') + link_obj = Link() + device_names, endpoints_data = dict(), dict() + else: + device_names, endpoints_data = get_endpoint_names(context_client, link_obj.link_endpoint_ids) context_client.close() - return render_template('link/detail.html',link=response, device_names=device_names, endpoints_data=endpoints_data) + return render_template('link/detail.html',link=link_obj, device_names=device_names, endpoints_data=endpoints_data) diff --git a/src/webui/service/service/routes.py b/src/webui/service/service/routes.py index ee9b092ae6828d7e2a82c66b1461c2f90853a803..defbe2cb003cc97830d6ec24db01bf8734a7f530 100644 --- a/src/webui/service/service/routes.py +++ b/src/webui/service/service/routes.py @@ -14,8 +14,11 @@ import grpc from flask import current_app, redirect, render_template, Blueprint, flash, session, url_for -from common.proto.context_pb2 import ContextId, Service, ServiceId, ServiceTypeEnum, ServiceStatusEnum, Connection +from common.proto.context_pb2 import ( + IsolationLevelEnum, Service, ServiceId, ServiceTypeEnum, ServiceStatusEnum, Connection) +from common.tools.context_queries.Context import get_context from common.tools.context_queries.EndPoint import get_endpoint_names +from common.tools.context_queries.Service import get_service from context.client.ContextClient import ContextClient from service.client.ServiceClient import ServiceClient @@ -26,93 +29,94 @@ service_client = ServiceClient() @service.get('/') def home(): - # flash('This is an info message', 'info') - # flash('This is a danger message', 'danger') - - context_uuid = session.get('context_uuid', '-') - if context_uuid == "-": + if 'context_uuid' not in session or 'topology_uuid' not in session: flash("Please select a context!", "warning") return redirect(url_for("main.home")) - request = ContextId() - request.context_uuid.uuid = context_uuid + context_uuid = session['context_uuid'] + context_client.connect() - try: - service_list = context_client.ListServices(request) - # print(service_list) - services = service_list.services - context_found = True - except grpc.RpcError as e: - if e.code() != grpc.StatusCode.NOT_FOUND: raise - if e.details() != 'Context({:s}) not found'.format(context_uuid): raise - services = [] - context_found = False - - if context_found: - endpoint_ids = [] - for service_ in services: - endpoint_ids.extend(service_.service_endpoint_ids) - device_names, endpoints_data = get_endpoint_names(context_client, endpoint_ids) + + context_obj = get_context(context_client, context_uuid, rw_copy=False) + if context_obj is None: + flash('Context({:s}) not found'.format(str(context_uuid)), 'danger') + services, device_names, endpoints_data = list(), list(), list() else: - device_names, endpoints_data = [],[] + try: + services = context_client.ListServices(context_obj.context_id) + services = services.services + except grpc.RpcError as e: + if e.code() != grpc.StatusCode.NOT_FOUND: raise + if e.details() != 'Context({:s}) not found'.format(context_uuid): raise + services, device_names, endpoints_data = list(), dict(), dict() + else: + endpoint_ids = list() + for service_ in services: + endpoint_ids.extend(service_.service_endpoint_ids) + device_names, endpoints_data = get_endpoint_names(context_client, endpoint_ids) context_client.close() return render_template( 'service/home.html', services=services, device_names=device_names, endpoints_data=endpoints_data, - context_not_found=not context_found, ste=ServiceTypeEnum, sse=ServiceStatusEnum) + 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') + #return render_template('service/home.html') @service.get('<path:service_uuid>/detail') def detail(service_uuid: str): - context_uuid = session.get('context_uuid', '-') - if context_uuid == "-": + if 'context_uuid' not in session or 'topology_uuid' not in session: flash("Please select a context!", "warning") return redirect(url_for("main.home")) - - request: ServiceId = ServiceId() - request.service_uuid.uuid = service_uuid - request.context_id.context_uuid.uuid = context_uuid + context_uuid = session['context_uuid'] + try: context_client.connect() - response: Service = context_client.GetService(request) - connections: Connection = context_client.ListConnections(request) - connections = connections.connections - endpoint_ids = [] - endpoint_ids.extend(response.service_endpoint_ids) - for connection in connections: - endpoint_ids.extend(connection.path_hops_endpoint_ids) - device_names, endpoints_data = get_endpoint_names(context_client, endpoint_ids) + endpoint_ids = list() + service_obj = get_service(context_client, service_uuid, rw_copy=False) + if service_obj is None: + flash('Context({:s})/Service({:s}) not found'.format(str(context_uuid), str(service_uuid)), 'danger') + service_obj = Service() + else: + endpoint_ids.extend(service_obj.service_endpoint_ids) + connections: Connection = context_client.ListConnections(service_obj.service_id) + connections = connections.connections + for connection in connections: endpoint_ids.extend(connection.path_hops_endpoint_ids) + + if len(endpoint_ids) > 0: + device_names, endpoints_data = get_endpoint_names(context_client, endpoint_ids) + else: + device_names, endpoints_data = dict(), dict() context_client.close() + + return render_template( + 'service/detail.html', service=service_obj, connections=connections, device_names=device_names, + endpoints_data=endpoints_data, ste=ServiceTypeEnum, sse=ServiceStatusEnum, ile=IsolationLevelEnum) 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, connections=connections, device_names=device_names, - endpoints_data=endpoints_data, ste=ServiceTypeEnum, sse=ServiceStatusEnum) @service.get('<path:service_uuid>/delete') def delete(service_uuid: str): - context_uuid = session.get('context_uuid', '-') - if context_uuid == "-": + if 'context_uuid' not in session or 'topology_uuid' not in session: flash("Please select a context!", "warning") return redirect(url_for("main.home")) + context_uuid = session['context_uuid'] try: request = ServiceId() request.service_uuid.uuid = service_uuid request.context_id.context_uuid.uuid = context_uuid service_client.connect() - response = service_client.DeleteService(request) + service_client.DeleteService(request) service_client.close() flash('Service "{:s}" deleted successfully!'.format(service_uuid), 'success') diff --git a/src/webui/service/slice/routes.py b/src/webui/service/slice/routes.py index 222508418a187bcab18f7d44fccf896c917c6821..cd1b672d5c1014b0e8aa301ed7b5a1f6d910f6df 100644 --- a/src/webui/service/slice/routes.py +++ b/src/webui/service/slice/routes.py @@ -11,11 +11,13 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# + import grpc from flask import current_app, redirect, render_template, Blueprint, flash, session, url_for -from common.proto.context_pb2 import ContextId, Slice, SliceId, SliceStatusEnum +from common.proto.context_pb2 import IsolationLevelEnum, Slice, SliceId, SliceStatusEnum +from common.tools.context_queries.Context import get_context from common.tools.context_queries.EndPoint import get_endpoint_names +from common.tools.context_queries.Slice import get_slice from context.client.ContextClient import ContextClient from slice.client.SliceClient import SliceClient @@ -26,92 +28,88 @@ slice_client = SliceClient() @slice.get('/') def home(): - context_uuid = session.get('context_uuid', '-') - if context_uuid == "-": + if 'context_uuid' not in session or 'topology_uuid' not in session: flash("Please select a context!", "warning") return redirect(url_for("main.home")) - request = ContextId() - request.context_uuid.uuid = context_uuid + context_uuid = session['context_uuid'] + context_client.connect() - try: - slice_list = context_client.ListSlices(request) - slices = slice_list.slices - context_found = True - except grpc.RpcError as e: - if e.code() != grpc.StatusCode.NOT_FOUND: raise - if e.details() != 'Context({:s}) not found'.format(context_uuid): raise - slices = [] - context_found = False - - if context_found: - endpoint_ids = [] - for slice_ in slices: - endpoint_ids.extend(slice_.slice_endpoint_ids) - device_names, endpoints_data = get_endpoint_names(context_client, endpoint_ids) + + context_obj = get_context(context_client, context_uuid, rw_copy=False) + if context_obj is None: + flash('Context({:s}) not found'.format(str(context_uuid)), 'danger') + device_names, endpoints_data = list(), list() else: - device_names, endpoints_data = [],[] + try: + slices = context_client.ListSlices(context_obj.context_id) + slices = slices.slices + except grpc.RpcError as e: + if e.code() != grpc.StatusCode.NOT_FOUND: raise + if e.details() != 'Context({:s}) not found'.format(context_uuid): raise + slices, device_names, endpoints_data = list(), dict(), dict() + else: + endpoint_ids = list() + for slice_ in slices: + endpoint_ids.extend(slice_.slice_endpoint_ids) + device_names, endpoints_data = get_endpoint_names(context_client, endpoint_ids) context_client.close() - return render_template( 'slice/home.html', slices=slices, device_names=device_names, endpoints_data=endpoints_data, - context_not_found=not context_found, sse=SliceStatusEnum) + sse=SliceStatusEnum) @slice.route('add', methods=['GET', 'POST']) def add(): flash('Add slice route called', 'danger') raise NotImplementedError() - return render_template('slice/home.html') + #return render_template('slice/home.html') @slice.get('<path:slice_uuid>/detail') def detail(slice_uuid: str): - context_uuid = session.get('context_uuid', '-') - if context_uuid == "-": + if 'context_uuid' not in session or 'topology_uuid' not in session: flash("Please select a context!", "warning") return redirect(url_for("main.home")) - - request: SliceId = SliceId() - request.slice_uuid.uuid = slice_uuid - request.context_id.context_uuid.uuid = context_uuid - req = ContextId() - req.context_uuid.uuid = context_uuid + context_uuid = session['context_uuid'] + try: context_client.connect() - response: Slice = context_client.GetSlice(request) - services = context_client.ListServices(req) - endpoint_ids = [] - endpoint_ids.extend(response.slice_endpoint_ids) - device_names, endpoints_data = get_endpoint_names(context_client, endpoint_ids) + slice_obj = get_slice(context_client, slice_uuid, rw_copy=False) + if slice_obj is None: + flash('Context({:s})/Slice({:s}) not found'.format(str(context_uuid), str(slice_uuid)), 'danger') + slice_obj = Slice() + else: + device_names, endpoints_data = get_endpoint_names(context_client, slice_obj.slice_endpoint_ids) context_client.close() + + return render_template( + 'slice/detail.html', slice=slice_obj, device_names=device_names, endpoints_data=endpoints_data, + sse=SliceStatusEnum, ile=IsolationLevelEnum) except Exception as e: flash('The system encountered an error and cannot show the details of this slice.', 'warning') current_app.logger.exception(e) return redirect(url_for('slice.home')) - return render_template( - 'slice/detail.html', slice=response, device_names=device_names, endpoints_data=endpoints_data, - sse=SliceStatusEnum, services=services) - -#@slice.get('<path:slice_uuid>/delete') -#def delete(slice_uuid: str): -# context_uuid = session.get('context_uuid', '-') -# if context_uuid == "-": -# flash("Please select a context!", "warning") -# return redirect(url_for("main.home")) -# -# try: -# request = SliceId() -# request.slice_uuid.uuid = slice_uuid -# request.context_id.context_uuid.uuid = context_uuid -# slice_client.connect() -# response = slice_client.DeleteSlice(request) -# slice_client.close() -# -# flash('Slice "{:s}" deleted successfully!'.format(slice_uuid), 'success') -# except Exception as e: -# flash('Problem deleting slice "{:s}": {:s}'.format(slice_uuid, str(e.details())), 'danger') -# current_app.logger.exception(e) -# return redirect(url_for('slice.home')) + +@slice.get('<path:slice_uuid>/delete') +def delete(slice_uuid: str): + if 'context_uuid' not in session or 'topology_uuid' not in session: + flash("Please select a context!", "warning") + return redirect(url_for("main.home")) + context_uuid = session['context_uuid'] + + try: + request = SliceId() + request.slice_uuid.uuid = slice_uuid + request.context_id.context_uuid.uuid = context_uuid + slice_client.connect() + slice_client.DeleteSlice(request) + slice_client.close() + + flash('Slice "{:s}" deleted successfully!'.format(slice_uuid), 'success') + except Exception as e: + flash('Problem deleting slice "{:s}": {:s}'.format(slice_uuid, str(e.details())), 'danger') + current_app.logger.exception(e) + return redirect(url_for('slice.home')) diff --git a/src/webui/service/templates/base.html b/src/webui/service/templates/base.html index 0aa022f1453eaa33a67212174cf9687a942b10f0..35999ebe1785a033097dd30bfd672ce3b9a91a87 100644 --- a/src/webui/service/templates/base.html +++ b/src/webui/service/templates/base.html @@ -103,7 +103,7 @@ </li> </ul> <span class="navbar-text" style="color: #fff;"> - Current Context(<b>{{ get_working_context() }}</b>)/Topology(<b>{{ get_working_topology() }}</b>) + Selected Context(<b>{{ get_working_context() }}</b>)/Topology(<b>{{ get_working_topology() }}</b>) </span> </div> </div> diff --git a/src/webui/service/templates/device/detail.html b/src/webui/service/templates/device/detail.html index de8bb4a81da5e595f33297070697b528dff26ff4..1b4b43f5ad12956ae8bb2b1a843ce5e57ef29a2c 100644 --- a/src/webui/service/templates/device/detail.html +++ b/src/webui/service/templates/device/detail.html @@ -29,13 +29,14 @@ <div class="col-sm-3"> <a id="update" class="btn btn-secondary" href="{{ url_for('device.update',device_uuid=device.device_id.device_uuid.uuid) }}"> <i class="bi bi-pencil-square"></i> - Update + Update device </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 + <i class="bi bi-x-square"></i> + Delete device </button> </div> </div> diff --git a/src/webui/service/templates/service/detail.html b/src/webui/service/templates/service/detail.html index b2160695173064b9863834a4d42c60a69cc913ba..d99ede3e02c9716782317efc60fcc8d92e2e811a 100644 --- a/src/webui/service/templates/service/detail.html +++ b/src/webui/service/templates/service/detail.html @@ -36,7 +36,8 @@ <div class="col-sm-3"> <!-- <button type="button" class="btn btn-danger"><i class="bi bi-x-square"></i>Delete service</button> --> <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal"> - <i class="bi bi-x-square"></i>Delete service + <i class="bi bi-x-square"></i> + Delete service </button> </div> </div> @@ -87,7 +88,7 @@ <thead> <tr> <th scope="col">Kind</th> - <th scope="col">Type</th> + <th scope="col">Key/Type</th> <th scope="col">Value</th> </tr> </thead> @@ -135,15 +136,43 @@ </td> <td>{{ constraint.endpoint_priority.priority }}</td> </tr> + {% elif constraint.WhichOneof('constraint')=='sla_capacity' %} + <tr> + <td>SLA Capacity</td> + <td>-</td> + <td> + {{ constraint.sla_capacity.capacity_gbps }} Gbps + </td> + </tr> + {% elif constraint.WhichOneof('constraint')=='sla_latency' %} + <tr> + <td>SLA E2E Latency</td> + <td>-</td> + <td> + {{ constraint.sla_latency.e2e_latency_ms }} ms + </td> + </tr> {% elif constraint.WhichOneof('constraint')=='sla_availability' %} <tr> <td>SLA Availability</td> <td>-</td> <td> + {{ constraint.sla_availability.availability }} %; {{ constraint.sla_availability.num_disjoint_paths }} disjoint paths; {% if constraint.sla_availability.all_active %}all{% else %}single{% endif %}-active </td> </tr> + {% elif constraint.WhichOneof('constraint')=='sla_isolation' %} + <tr> + <td>SLA Isolation</td> + <td>-</td> + <td> + {% for i,isolation_level in enumerate(constraint.sla_isolation.isolation_level) %} + {% if i > 0 %}, {% endif %} + {{ ile.Name(isolation_level) }} + {% endfor %} + </td> + </tr> {% else %} <tr> <td>-</td> @@ -185,34 +214,12 @@ {% endfor %} </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"> - <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <h5 class="modal-title" id="staticBackdropLabel">Delete service?</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 service "{{ service.service_id.service_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('service.delete', service_uuid=service.service_id.service_uuid.uuid) }}"><i - class="bi bi-exclamation-diamond"></i>Yes</a> - </div> - </div> - </div> -</div> - <table class="table table-striped table-hover"> <thead> <tr> <th scope="col">Connection Id</th> - <th scope="col">Sub-service</th> + <th scope="col">Sub-Service</th> <th scope="col">Path</th> </tr> </thead> @@ -258,8 +265,26 @@ </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"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="staticBackdropLabel">Delete service?</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 service "{{ service.service_id.service_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('service.delete', service_uuid=service.service_id.service_uuid.uuid) }}"><i + class="bi bi-exclamation-diamond"></i>Yes</a> + </div> + </div> + </div> +</div> - - - -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/src/webui/service/templates/slice/detail.html b/src/webui/service/templates/slice/detail.html index 390f882d7058b825ecf9d2bce5689585f99b80aa..6c8d15aed6fcf91580e9fa3bfe9f2f9a14e7666b 100644 --- a/src/webui/service/templates/slice/detail.html +++ b/src/webui/service/templates/slice/detail.html @@ -32,14 +32,14 @@ <i class="bi bi-pencil-square"></i> Update </a> - </div> - <div class="col-sm-3">--> + </div>--> + <div class="col-sm-3"> <!-- <button type="button" class="btn btn-danger"><i class="bi bi-x-square"></i>Delete slice</button> --> - <!--<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal"> - <i class="bi bi-x-square"></i>Delete slice + <button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal"> + <i class="bi bi-x-square"></i> + Delete slice </button> </div> - --> </div> <div class="row mb-3"> @@ -88,7 +88,7 @@ <thead> <tr> <th scope="col">Kind</th> - <th scope="col">Type</th> + <th scope="col">Key/Type</th> <th scope="col">Value</th> </tr> </thead> @@ -136,15 +136,43 @@ </td> <td>{{ constraint.endpoint_priority.priority }}</td> </tr> + {% elif constraint.WhichOneof('constraint')=='sla_capacity' %} + <tr> + <td>SLA Capacity</td> + <td>-</td> + <td> + {{ constraint.sla_capacity.capacity_gbps }} Gbps + </td> + </tr> + {% elif constraint.WhichOneof('constraint')=='sla_latency' %} + <tr> + <td>SLA E2E Latency</td> + <td>-</td> + <td> + {{ constraint.sla_latency.e2e_latency_ms }} ms + </td> + </tr> {% elif constraint.WhichOneof('constraint')=='sla_availability' %} <tr> <td>SLA Availability</td> <td>-</td> <td> + {{ constraint.sla_availability.availability }} %; {{ constraint.sla_availability.num_disjoint_paths }} disjoint paths; {% if constraint.sla_availability.all_active %}all{% else %}single{% endif %}-active </td> </tr> + {% elif constraint.WhichOneof('constraint')=='sla_isolation' %} + <tr> + <td>SLA Isolation</td> + <td>-</td> + <td> + {% for i,isolation_level in enumerate(constraint.sla_isolation.isolation_level) %} + {% if i > 0 %}, {% endif %} + {{ ile.Name(isolation_level) }} + {% endfor %} + </td> + </tr> {% else %} <tr> <td>-</td> @@ -191,7 +219,7 @@ <table class="table table-striped table-hover"> <thead> <tr> - <th scope="col">Service Id</th> + <th scope="col">Sub-Services</th> </tr> </thead> <tbody> @@ -219,7 +247,7 @@ <table class="table table-striped table-hover"> <thead> <tr> - <th scope="col">Sub-slices</th> + <th scope="col">Sub-Slices</th> </tr> </thead> <tbody> @@ -244,4 +272,27 @@ </table> </div> </div> -{% endblock %} \ No newline at end of file + +<!-- 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 slice?</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 slice "{{ slice.slice_id.slice_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('slice.delete', slice_uuid=slice.slice_id.slice_uuid.uuid) }}"><i + class="bi bi-exclamation-diamond"></i>Yes</a> + </div> + </div> + </div> +</div> + +{% endblock %}