diff --git a/src/webui/service/service/routes.py b/src/webui/service/service/routes.py index 1d3e254905ef3fec9f78b06eb843a112c1c8edff..e0d7b4c31ad69963e0782be3d59eab9e9b74b44e 100644 --- a/src/webui/service/service/routes.py +++ b/src/webui/service/service/routes.py @@ -12,36 +12,38 @@ # See the License for the specific language governing permissions and # limitations under the License. - -import json, logging #, re -import json -import grpc +import grpc, json, logging #, re from collections import defaultdict from flask import current_app, redirect, render_template, Blueprint, flash, session, url_for, request +from typing import Optional, Set +from wtforms.validators import ValidationError + from common.proto.context_pb2 import ( - ContextId, IsolationLevelEnum, Service, ServiceId, ServiceTypeEnum, ServiceStatusEnum, Connection, Empty, DeviceDriverEnum, - ConfigActionEnum, Device, DeviceList) + ContextId, IsolationLevelEnum, Service, ServiceId, ServiceTypeEnum, ServiceStatusEnum, Connection, + Empty, DeviceDriverEnum, ConfigActionEnum, Device, DeviceList +) from common.tools.context_queries.Context import get_context -from common.tools.context_queries.Topology import get_topology from common.tools.context_queries.EndPoint import get_endpoint_names -from wtforms.validators import ValidationError -from context.client.ContextClient import ContextClient -from service.client.ServiceClient import ServiceClient -from device.client.DeviceClient import DeviceClient -from common.tools.object_factory.Service import ( - json_service_l2nm_planned, json_service_l3nm_planned) -from common.tools.object_factory.Constraint import ( - json_constraint_sla_availability, json_constraint_sla_capacity, json_constraint_sla_isolation, - json_constraint_sla_latency) +from common.tools.context_queries.Service import get_service_by_uuid +from common.tools.context_queries.Topology import get_topology from common.tools.descriptor.Loader import DescriptorLoader, compose_notifications from common.tools.object_factory.ConfigRule import json_config_rule_set +from common.tools.object_factory.Constraint import ( + json_constraint_sla_availability, json_constraint_sla_capacity, json_constraint_sla_isolation, + json_constraint_sla_latency +) +from common.tools.object_factory.Context import json_context_id from common.tools.object_factory.Device import json_device_id from common.tools.object_factory.EndPoint import json_endpoint_id -from webui.service.service.forms import AddServiceForm_1, AddServiceForm_ACL_L2, AddServiceForm_ACL_IPV4, AddServiceForm_ACL_IPV6, AddServiceForm_L2VPN, AddServiceForm_L3VPN -from common.tools.context_queries.Service import get_service_by_uuid -from common.tools.object_factory.Context import json_context_id +from common.tools.object_factory.Service import json_service_l2nm_planned, json_service_l3nm_planned from common.tools.object_factory.Topology import json_topology_id -from typing import Optional, Set +from context.client.ContextClient import ContextClient +from device.client.DeviceClient import DeviceClient +from service.client.ServiceClient import ServiceClient +from webui.service.service.forms import ( + AddServiceForm_1, AddServiceForm_ACL_L2, AddServiceForm_ACL_IPV4, AddServiceForm_ACL_IPV6, + AddServiceForm_L2VPN, AddServiceForm_L3VPN +) LOGGER = logging.getLogger(__name__) service = Blueprint('service', __name__, url_prefix='/service') @@ -50,21 +52,10 @@ context_client = ContextClient() service_client = ServiceClient() device_client = DeviceClient() -type = ["ACL_UNDEFINED", "ACL_IPV4","ACL_IPV6","ACL_L2","ACL_MPLS","ACL_MIXED"] +ACL_TYPE = ["ACL_UNDEFINED", "ACL_IPV4","ACL_IPV6","ACL_L2","ACL_MPLS","ACL_MIXED"] f_action = ["UNDEFINED", "DROP","ACCEPT","REJECT"] l_action = ["UNDEFINED", "LOG_NONE","LOG_SYSLOG"] -''' -@service.get('/') #Route for the homepage of the created "service" blueprint -@contextmanager -def connected_client(c): - try: - c.connect() - yield c - finally: - c.close() -''' - def get_device_drivers_in_use(topology_uuid: str, context_uuid: str) -> Set[str]: active_drivers = set() grpc_topology = get_topology(context_client, topology_uuid, context_uuid=context_uuid, rw_copy=False) @@ -118,13 +109,15 @@ def add(): def get_hub_module_name(dev: Device) -> Optional[str]: for cr in dev.device_config.config_rules: - if cr.action == ConfigActionEnum.CONFIGACTION_SET and cr.custom and cr.custom.resource_key == "_connect/settings": - try: - cr_dict = json.loads(cr.custom.resource_value) - if "hub_module_name" in cr_dict: - return cr_dict["hub_module_name"] - except json.JSONDecodeError: - pass + if cr.action != ConfigActionEnum.CONFIGACTION_SET: continue + if not cr.custom: continue + if cr.custom.resource_key != "_connect/settings": continue + try: + cr_dict = json.loads(cr.custom.resource_value) + if "hub_module_name" in cr_dict: + return cr_dict["hub_module_name"] + except json.JSONDecodeError: + pass return None @service.route('add-xr', methods=['GET', 'POST']) @@ -142,122 +135,137 @@ def add_xr(): if grpc_topology is None: flash('Context({:s})/Topology({:s}) not found'.format(str(context_uuid), str(topology_uuid)), 'danger') return redirect(url_for("main.home")) - else: - topo_device_uuids = {device_id.device_uuid.uuid for device_id in grpc_topology.device_ids} - grpc_devices= context_client.ListDevices(Empty()) - devices = [ - device for device in grpc_devices.devices - if device.device_id.device_uuid.uuid in topo_device_uuids and DeviceDriverEnum.DEVICEDRIVER_XR in device.device_drivers - ] - devices.sort(key=lambda dev: dev.name) - - hub_interfaces_by_device = defaultdict(list) - leaf_interfaces_by_device = defaultdict(list) - constellation_name_to_uuid = {} - dev_ep_to_uuid = {} - ep_uuid_to_name = {} - for d in devices: - constellation_name_to_uuid[d.name] = d.device_id.device_uuid.uuid - hm_name = get_hub_module_name(d) - if hm_name is not None: - hm_if_prefix= hm_name + "|" - for ep in d.device_endpoints: - dev_ep_to_uuid[(d.name, ep.name)] = ep.endpoint_id.endpoint_uuid.uuid - if ep.name.startswith(hm_if_prefix): - hub_interfaces_by_device[d.name].append(ep.name) - else: - leaf_interfaces_by_device[d.name].append(ep.name) - ep_uuid_to_name[ep.endpoint_id.endpoint_uuid.uuid] = (d.name, ep.name) - hub_interfaces_by_device[d.name].sort() - leaf_interfaces_by_device[d.name].sort() - - 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') - return redirect(request.url) - - services = context_client.ListServices(context_obj.context_id) - ep_used_by={} - for service in services.services: - if service.service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE: - for ep in service.service_endpoint_ids: - ep_uuid = ep.endpoint_uuid.uuid - if ep_uuid in ep_uuid_to_name: - dev_name, ep_name = ep_uuid_to_name[ep_uuid] - ep_used_by[f"{ep_name}@{dev_name}"] = service.name + + topo_device_uuids = {device_id.device_uuid.uuid for device_id in grpc_topology.device_ids} + grpc_devices= context_client.ListDevices(Empty()) + devices = [ + device for device in grpc_devices.devices + if device.device_id.device_uuid.uuid in topo_device_uuids and DeviceDriverEnum.DEVICEDRIVER_XR in device.device_drivers + ] + devices.sort(key=lambda dev: dev.name) + + hub_interfaces_by_device = defaultdict(list) + leaf_interfaces_by_device = defaultdict(list) + constellation_name_to_uuid = {} + dev_ep_to_uuid = {} + ep_uuid_to_name = {} + for d in devices: + constellation_name_to_uuid[d.name] = d.device_id.device_uuid.uuid + hm_name = get_hub_module_name(d) + if hm_name is not None: + hm_if_prefix= hm_name + "|" + for ep in d.device_endpoints: + dev_ep_to_uuid[(d.name, ep.name)] = ep.endpoint_id.endpoint_uuid.uuid + if ep.name.startswith(hm_if_prefix): + hub_interfaces_by_device[d.name].append(ep.name) + else: + leaf_interfaces_by_device[d.name].append(ep.name) + ep_uuid_to_name[ep.endpoint_id.endpoint_uuid.uuid] = (d.name, ep.name) + hub_interfaces_by_device[d.name].sort() + leaf_interfaces_by_device[d.name].sort() + + 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') + return redirect(request.url) + + services = context_client.ListServices(context_obj.context_id) + ep_used_by={} + for service in services.services: + if service.service_type == ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE: + for ep in service.service_endpoint_ids: + ep_uuid = ep.endpoint_uuid.uuid + if ep_uuid in ep_uuid_to_name: + dev_name, ep_name = ep_uuid_to_name[ep_uuid] + ep_used_by[f"{ep_name}@{dev_name}"] = service.name context_client.close() if request.method != 'POST': - return render_template('service/add-xr.html', devices=devices, hub_if=hub_interfaces_by_device, leaf_if=leaf_interfaces_by_device, ep_used_by=ep_used_by) - else: - service_name = request.form["service_name"] - if service_name == "": - flash(f"Service name must be specified", 'danger') - - constellation = request.form["constellation"] - constellation_uuid = constellation_name_to_uuid.get(constellation, None) - if constellation_uuid is None: - flash(f"Invalid constellation \"{constellation}\"", 'danger') - - hub_if = request.form["hubif"] - hub_if_uuid = dev_ep_to_uuid.get((constellation, hub_if), None) - if hub_if_uuid is None: - flash(f"Invalid hub interface \"{hub_if}\"", 'danger') - - leaf_if = request.form["leafif"] - leaf_if_uuid = dev_ep_to_uuid.get((constellation, leaf_if), None) - if leaf_if_uuid is None: - flash(f"Invalid leaf interface \"{leaf_if}\"", 'danger') - - if service_name == "" or constellation_uuid is None or hub_if_uuid is None or leaf_if_uuid is None: + return render_template( + 'service/add-xr.html', devices=devices, hub_if=hub_interfaces_by_device, + leaf_if=leaf_interfaces_by_device, ep_used_by=ep_used_by + ) + + service_name = request.form["service_name"] + if service_name == "": + flash(f"Service name must be specified", 'danger') + + constellation = request.form["constellation"] + constellation_uuid = constellation_name_to_uuid.get(constellation, None) + if constellation_uuid is None: + flash(f"Invalid constellation \"{constellation}\"", 'danger') + + hub_if = request.form["hubif"] + hub_if_uuid = dev_ep_to_uuid.get((constellation, hub_if), None) + if hub_if_uuid is None: + flash(f"Invalid hub interface \"{hub_if}\"", 'danger') + + leaf_if = request.form["leafif"] + leaf_if_uuid = dev_ep_to_uuid.get((constellation, leaf_if), None) + if leaf_if_uuid is None: + flash(f"Invalid leaf interface \"{leaf_if}\"", 'danger') + + if service_name == "" or constellation_uuid is None or hub_if_uuid is None or leaf_if_uuid is None: + return redirect(request.url) + + + json_context_uuid=json_context_id(context_uuid) + sr = { + "name": service_name, + "service_id": { + "context_id": {"context_uuid": {"uuid": context_uuid}}, + "service_uuid": {"uuid": service_name} + }, + 'service_type' : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, + "service_endpoint_ids": [ + { + 'device_id': {'device_uuid': {'uuid': constellation_uuid}}, + 'endpoint_uuid': {'uuid': hub_if_uuid}, + 'topology_id': json_topology_id("admin", context_id=json_context_uuid) + }, + { + 'device_id': {'device_uuid': {'uuid': constellation_uuid}}, + 'endpoint_uuid': {'uuid': leaf_if_uuid}, + 'topology_id': json_topology_id("admin", context_id=json_context_uuid) + } + ], + 'service_status' : {'service_status': ServiceStatusEnum.SERVICESTATUS_PLANNED}, + 'service_constraints' : [], + } + + json_tapi_settings = { + 'capacity_value' : 50.0, + 'capacity_unit' : 'GHz', + 'layer_proto_name': 'PHOTONIC_MEDIA', + 'layer_proto_qual': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC', + 'direction' : 'UNIDIRECTIONAL', + } + config_rule = json_config_rule_set('/settings', json_tapi_settings) + + try: + service_client.connect() + + endpoints, sr['service_endpoint_ids'] = sr['service_endpoint_ids'], [] + try: + create_response = service_client.CreateService(Service(**sr)) + except Exception as e: + flash(f'Failure to update service name {service_name} with endpoints and configuration, exception {str(e)}', 'danger') return redirect(request.url) - - json_context_uuid=json_context_id(context_uuid) - sr = { - "name": service_name, - "service_id": { - "context_id": {"context_uuid": {"uuid": context_uuid}}, - "service_uuid": {"uuid": service_name} - }, - 'service_type' : ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE, - "service_endpoint_ids": [ - {'device_id': {'device_uuid': {'uuid': constellation_uuid}}, 'endpoint_uuid': {'uuid': hub_if_uuid}, 'topology_id': json_topology_id("admin", context_id=json_context_uuid)}, - {'device_id': {'device_uuid': {'uuid': constellation_uuid}}, 'endpoint_uuid': {'uuid': leaf_if_uuid}, 'topology_id': json_topology_id("admin", context_id=json_context_uuid)} - ], - 'service_status' : {'service_status': ServiceStatusEnum.SERVICESTATUS_PLANNED}, - 'service_constraints' : [], - } + sr['service_endpoint_ids'] = endpoints + sr['service_config'] = {'config_rules': [config_rule]} - json_tapi_settings = { - 'capacity_value' : 50.0, - 'capacity_unit' : 'GHz', - 'layer_proto_name': 'PHOTONIC_MEDIA', - 'layer_proto_qual': 'tapi-photonic-media:PHOTONIC_LAYER_QUALIFIER_NMC', - 'direction' : 'UNIDIRECTIONAL', - } - config_rule = json_config_rule_set('/settings', json_tapi_settings) - - with connected_client(service_client) as sc: - endpoints, sr['service_endpoint_ids'] = sr['service_endpoint_ids'], [] - try: - create_response = sc.CreateService(Service(**sr)) - except Exception as e: - flash(f'Failure to update service name {service_name} with endpoints and configuration, exception {str(e)}', 'danger') - return redirect(request.url) - - sr['service_endpoint_ids'] = endpoints - sr['service_config'] = {'config_rules': [config_rule]} - - try: - update_response = sc.UpdateService(Service(**sr)) - flash(f'Created service {update_response.service_uuid.uuid}', 'success') - except Exception as e: - flash(f'Failure to update service {create_response.service_uuid.uuid} with endpoints and configuration, exception {str(e)}', 'danger') - return redirect(request.url) + try: + update_response = service_client.UpdateService(Service(**sr)) + flash(f'Created service {update_response.service_uuid.uuid}', 'success') + except Exception as e: + flash(f'Failure to update service {create_response.service_uuid.uuid} with endpoints and configuration, exception {str(e)}', 'danger') + return redirect(request.url) - return redirect(url_for('service.home')) + return redirect(url_for('service.home')) + finally: + service_client.close() @service.get('<path:service_uuid>/detail') def detail(service_uuid: str): @@ -287,7 +295,9 @@ def detail(service_uuid: str): 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, type = type, f_action = f_action, l_action = l_action) + endpoints_data=endpoints_data, ste=ServiceTypeEnum, sse=ServiceStatusEnum, ile=IsolationLevelEnum, + type=ACL_TYPE, f_action=f_action, l_action=l_action + ) except Exception as e: flash('The system encountered an error and cannot show the details of this service.', 'warning') current_app.logger.exception(e) @@ -318,16 +328,9 @@ def delete(service_uuid: str): def add_configure(): form_1 = AddServiceForm_1() if form_1.validate_on_submit(): - if form_1.service_type.data == 'ACL_L2': - return redirect(url_for('service.add_configure_ACL_L2')) - elif form_1.service_type.data == 'ACL_IPV4': - return redirect(url_for('service.add_configure_ACL_IPV4')) - elif form_1.service_type.data == 'ACL_IPV6': - return redirect(url_for('service.add_configure_ACL_IPV6')) - elif form_1.service_type.data == 'L2VPN': - return redirect(url_for('service.add_configure_L2VPN')) - elif form_1.service_type.data == 'L3VPN': - return redirect(url_for('service.add_configure_L3VPN')) + service_type = str(form_1.service_type.data) + if service_type in {'ACL_L2', 'ACL_IPV4', 'ACL_IPV6', 'L2VPN', 'L3VPN'}: + return redirect(url_for('service.add_configure_{:s}'.format(service_type))) return render_template('service/add.html', form_1=form_1, submit_text='Continue to configuraton') @service.route('add/configure/ACL_L2', methods=['GET', 'POST']) @@ -595,15 +598,13 @@ def get_device_vendor(form, devices): return vendor_value_1, vendor_value_2 def validate_params_vendor(form, vendor, device_num): - if vendor == "ADVA": - if form.NI_name.data != f"ELAN-AC:{getattr(form, f'Device_{device_num}_IF_vlan_id').data}": - raise ValidationError('For an ADVA device, the name of the Network Instance should have this name: "ELAN-AC:vlanID"') + if vendor != "ADVA": return - elif getattr(form, f'Device_{device_num}_NI_VC_ID').data != getattr(form, f'Device_{device_num}_IF_vlan_id').data: - raise ValidationError('For an ADVA device, the value of the VlanID and the value of the VC_ID must be the same') - else: - None - return None + if form.NI_name.data != f"ELAN-AC:{getattr(form, f'Device_{device_num}_IF_vlan_id').data}": + raise ValidationError('For an ADVA device, the name of the Network Instance should have this name: "ELAN-AC:vlanID"') + + elif getattr(form, f'Device_{device_num}_NI_VC_ID').data != getattr(form, f'Device_{device_num}_IF_vlan_id').data: + raise ValidationError('For an ADVA device, the value of the VlanID and the value of the VC_ID must be the same') def set_service_parameters(service_obj, form, selected_device_1, selected_device_2, selected_endpoint_1, selected_endpoint_2): service_obj.service_id.service_uuid.uuid = str(form.service_name.data)