Skip to content
routes.py 7.39 KiB
Newer Older
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (https://tfs.etsi.org/)
#
# 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.
Lucie LONG's avatar
Lucie LONG committed

Pablo Armingol's avatar
Pablo Armingol committed
import base64, json, logging #, re
from flask import jsonify, redirect, render_template, Blueprint, flash, session, url_for, request
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
from common.proto.context_pb2 import ContextList, Empty, TopologyId, TopologyList
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
from common.tools.descriptor.Loader import DescriptorLoader, compose_notifications
Lucie LONG's avatar
Lucie LONG committed
from common.tools.grpc.Tools import grpc_message_to_json_string
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
from common.tools.object_factory.Context import json_context_id
from common.tools.object_factory.Topology import json_topology_id
from context.client.ContextClient import ContextClient
from device.client.DeviceClient import DeviceClient
Lucie LONG's avatar
Lucie LONG committed
from service.client.ServiceClient import ServiceClient
Lucie LONG's avatar
Lucie LONG committed
from slice.client.SliceClient import SliceClient
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
from webui.service.main.forms import ContextTopologyForm, DescriptorForm
Lucie LONG's avatar
Lucie LONG committed

main = Blueprint('main', __name__)
Lucie LONG's avatar
Lucie LONG committed

context_client = ContextClient()
device_client = DeviceClient()
Lucie LONG's avatar
Lucie LONG committed
service_client = ServiceClient()
Lucie LONG's avatar
Lucie LONG committed
slice_client = SliceClient()

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
LOGGER = logging.getLogger(__name__)
Lucie LONG's avatar
Lucie LONG committed

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
DESCRIPTOR_LOADER_NUM_WORKERS = 10

def process_descriptors(descriptors):
    try:
        descriptors_file = request.files[descriptors.name]
        descriptors_data = descriptors_file.read()
        descriptors = json.loads(descriptors_data)
    except Exception as e: # pylint: disable=broad-except
        flash(f'Unable to load descriptor file: {str(e)}', 'danger')
        return
Lucie LONG's avatar
Lucie LONG committed

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    descriptor_loader = DescriptorLoader(descriptors, num_workers=DESCRIPTOR_LOADER_NUM_WORKERS)
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    results = descriptor_loader.process()
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    for message,level in compose_notifications(results):
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        if level == 'error': LOGGER.warning('ERROR message={:s}'.format(str(message)))
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        flash(message, level)
Lucie LONG's avatar
Lucie LONG committed

@main.route('/', methods=['GET', 'POST'])
def home():
    context_client.connect()
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    context_topology_form = ContextTopologyForm()
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    context_topology_form.context_topology.choices.append(('', 'Select...'))
Lucie LONG's avatar
Lucie LONG committed

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    contexts : ContextList = context_client.ListContexts(Empty())
    for context_ in contexts.contexts:
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        #context_uuid : str = context_.context_id.context_uuid.uuid
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        context_name : str = context_.name
        topologies : TopologyList = context_client.ListTopologies(context_.context_id)
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        for topology_ in topologies.topologies:
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            #topology_uuid : str = topology_.topology_id.topology_uuid.uuid
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            topology_name : str = topology_.name
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            raw_values = context_name, topology_name
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            b64_values = [base64.b64encode(v.encode('utf-8')).decode('utf-8') for v in raw_values]
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            context_topology_uuid = ','.join(b64_values)
            context_topology_name  = 'Context({:s}):Topology({:s})'.format(context_name, topology_name)
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            context_topology_entry = (context_topology_uuid, context_topology_name)
            context_topology_form.context_topology.choices.append(context_topology_entry)
Lucie LONG's avatar
Lucie LONG committed

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    if context_topology_form.validate_on_submit():
        context_topology_uuid = context_topology_form.context_topology.data
        if len(context_topology_uuid) > 0:
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            b64_values = context_topology_uuid.split(',')
            raw_values = [base64.b64decode(v.encode('utf-8')).decode('utf-8') for v in b64_values]
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            context_name, topology_name = raw_values
            #session.clear()
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            session['context_topology_uuid'] = context_topology_uuid
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            session['context_uuid'] = context_name
            #session['context_name'] = context_name
            session['topology_uuid'] = topology_name
            #session['topology_name'] = topology_name
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            MSG = f'Context({context_name})/Topology({topology_name}) successfully selected.'
            flash(MSG, 'success')
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed

            context_client.close()
            device_client.close()

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            return redirect(url_for('main.home'))

            #match = re.match('ctx\[([^\]]+)\]\/topo\[([^\]]+)\]', context_topology_uuid)
            #if match is not None:
            #    session['context_topology_uuid'] = context_topology_uuid = match.group(0)
            #    session['context_uuid'] = context_uuid = match.group(1)
            #    session['topology_uuid'] = topology_uuid = match.group(2)
            #    MSG = f'Context({context_uuid})/Topology({topology_uuid}) successfully selected.'
            #    flash(MSG, 'success')
            #    return redirect(url_for('main.home'))
Lucie LONG's avatar
Lucie LONG committed

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    if 'context_topology_uuid' in session:
        context_topology_form.context_topology.data = session['context_topology_uuid']
Lucie LONG's avatar
Lucie LONG committed

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    descriptor_form = DescriptorForm()
    try:
        if descriptor_form.validate_on_submit():
            process_descriptors(descriptor_form.descriptors)
            return redirect(url_for("main.home"))
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    except Exception as e: # pylint: disable=broad-except
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        LOGGER.exception('Descriptor load failed')
        flash(f'Descriptor load failed: `{str(e)}`', 'danger')
    finally:
        context_client.close()
        device_client.close()
Lucie LONG's avatar
Lucie LONG committed

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    return render_template(
        'main/home.html', context_topology_form=context_topology_form, descriptor_form=descriptor_form)
Lucie LONG's avatar
Lucie LONG committed

@main.route('/topology', methods=['GET'])
def topology():
    context_client.connect()
    try:
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        if 'context_uuid' not in session or 'topology_uuid' not in session:
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
            return jsonify({'devices': [], 'links': []})

        context_uuid = session['context_uuid']
        topology_uuid = session['topology_uuid']

        json_topo_id = json_topology_id(topology_uuid, context_id=json_context_id(context_uuid))
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        response = context_client.GetTopologyDetails(TopologyId(**json_topo_id))
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed

        devices = []
        for device in response.devices:
            devices.append({
                'id': device.device_id.device_uuid.uuid,
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
                'name': device.name,
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
                'type': device.device_type,
            })
Lucie LONG's avatar
Lucie LONG committed

Lucie LONG's avatar
Lucie LONG committed
        links = []
        for link in response.links:
            if len(link.link_endpoint_ids) != 2:
                str_link = grpc_message_to_json_string(link)
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
                LOGGER.warning('Unexpected link with len(endpoints) != 2: {:s}'.format(str_link))
Lucie LONG's avatar
Lucie LONG committed
                continue
            links.append({
                'id': link.link_id.link_uuid.uuid,
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
                'name': link.name,
Lucie LONG's avatar
Lucie LONG committed
                'source': link.link_endpoint_ids[0].device_id.device_uuid.uuid,
                'target': link.link_endpoint_ids[1].device_id.device_uuid.uuid,
            })
Lucie LONG's avatar
Lucie LONG committed

        return jsonify({'devices': devices, 'links': links})
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    except: # pylint: disable=bare-except
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        LOGGER.exception('Error retrieving topology')
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        return jsonify({'devices': [], 'links': []})
Lucie LONG's avatar
Lucie LONG committed

@main.get('/about')
def about():
    return render_template('main/about.html')
Lucie LONG's avatar
Lucie LONG committed

Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
@main.get('/debug')
def debug():
    return render_template('main/debug.html')
Lucie LONG's avatar
Lucie LONG committed

@main.get('/resetsession')
def reset_session():
    session.clear()
Lucie LONG's avatar
Lucie LONG committed
    return redirect(url_for("main.home"))