diff --git a/src/webui/service/main/DescriptorTools.py b/src/webui/service/main/DescriptorTools.py new file mode 100644 index 0000000000000000000000000000000000000000..31bb793d82be604a96a4d67f9fd40a6b4cbc1579 --- /dev/null +++ b/src/webui/service/main/DescriptorTools.py @@ -0,0 +1,75 @@ +# 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. + +import copy +from typing import Dict, List, Optional, Tuple + +def get_descriptors_add_contexts(contexts : List[Dict]) -> List[Dict]: + contexts_add = copy.deepcopy(contexts) + for context in contexts_add: + context['topology_ids'] = [] + context['service_ids'] = [] + return contexts_add + +def get_descriptors_add_topologies(topologies : List[Dict]) -> List[Dict]: + topologies_add = copy.deepcopy(topologies) + for topology in topologies_add: + topology['device_ids'] = [] + topology['link_ids'] = [] + return topologies_add + +def get_descriptors_add_services(services : List[Dict]) -> List[Dict]: + 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) + return services_add + +def get_descriptors_add_slices(slices : List[Dict]) -> List[Dict]: + 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) + return slices_add + +def split_devices_by_rules(devices : List[Dict]) -> Tuple[List[Dict], List[Dict]]: + devices_add = [] + devices_config = [] + for device in devices: + connect_rules = [] + config_rules = [] + for config_rule in device.get('device_config', {}).get('config_rules', []): + custom_resource_key : Optional[str] = config_rule.get('custom', {}).get('resource_key') + if custom_resource_key is not None and custom_resource_key.startswith('_connect/'): + connect_rules.append(config_rule) + else: + config_rules.append(config_rule) + + if len(connect_rules) > 0: + device_add = copy.deepcopy(device) + device_add['device_endpoints'] = [] + device_add['device_config'] = {'config_rules': connect_rules} + devices_add.append(device_add) + + if len(config_rules) > 0: + device['device_config'] = {'config_rules': config_rules} + devices_config.append(device) + + return devices_add, devices_config diff --git a/src/webui/service/main/routes.py b/src/webui/service/main/routes.py index d115444487c7356a541f3c567e5dea183da73ade..80dfb30c2bdd8d75937611bf0dfbd8e3426e4847 100644 --- a/src/webui/service/main/routes.py +++ b/src/webui/service/main/routes.py @@ -12,8 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import copy, json, logging -from typing import Optional +import json, logging from flask import jsonify, redirect, render_template, Blueprint, flash, session, url_for, request 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 @@ -21,6 +20,9 @@ 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.DescriptorTools import ( + get_descriptors_add_contexts, get_descriptors_add_services, get_descriptors_add_slices, + get_descriptors_add_topologies, split_devices_by_rules) from webui.service.main.forms import ContextForm, DescriptorForm main = Blueprint('main', __name__) @@ -82,94 +84,54 @@ def process_descriptors(descriptors): slices = descriptors.get('slices' , []) connections = descriptors.get('connections', []) + # Context and Topology require to create the entity first, and add devices, links, services, slices, etc. in a + # second stage. + contexts_add = get_descriptors_add_contexts(contexts) + topologies_add = get_descriptors_add_topologies(topologies) + 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 ) + process_descriptor('context', 'update', context_client.SetContext, Context, contexts ) + process_descriptor('topology', 'update', context_client.SetTopology, Topology, topologies ) context_client.close() - return + else: + # Normal mode: follows the automated workflows in the different components + assert len(connections) == 0, 'in normal mode, connections should not be set' - # Normal mode: follows the automated workflows in the different components - - # in normal mode, connections should not be set - assert len(connections) == 0 - - devices_add = [] - devices_config = [] - for device in devices: - connect_rules = [] - config_rules = [] - for config_rule in device.get('device_config', {}).get('config_rules', []): - custom_resource_key : Optional[str] = config_rule.get('custom', {}).get('resource_key') - if custom_resource_key is not None and custom_resource_key.startswith('_connect/'): - connect_rules.append(config_rule) - else: - config_rules.append(config_rule) - - if len(connect_rules) > 0: - device_add = copy.deepcopy(device) - device_add['device_endpoints'] = [] - device_add['device_config'] = {'config_rules': connect_rules} - devices_add.append(device_add) - - if len(config_rules) > 0: - device['device_config'] = {'config_rules': config_rules} - devices_config.append(device) - - 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) - - 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) + # Device, Service and Slice require to first create the entity and the configure it + devices_add, devices_config = split_devices_by_rules(devices) + services_add = get_descriptors_add_services(services) + slices_add = get_descriptors_add_slices(slices) - 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_add ) - process_descriptor('device', 'config', device_client .ConfigureDevice, Device, devices_config) - 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() + context_client.connect() + device_client.connect() + service_client.connect() + slice_client.connect() + + process_descriptor('context', 'add', context_client.SetContext, Context, contexts_add ) + process_descriptor('topology', 'add', context_client.SetTopology, Topology, topologies_add) + process_descriptor('device', 'add', device_client .AddDevice, Device, devices_add ) + process_descriptor('device', 'config', device_client .ConfigureDevice, Device, devices_config) + 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 ) + process_descriptor('context', 'update', context_client.SetContext, Context, contexts ) + process_descriptor('topology', 'update', context_client.SetTopology, Topology, topologies ) + + slice_client.close() + service_client.close() + device_client.close() + context_client.close() @main.route('/', methods=['GET', 'POST']) def home():