Skip to content
routes.py 9.72 KiB
Newer Older
# 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.

from flask import current_app, render_template, Blueprint, flash, session, redirect, url_for
from common.proto.context_pb2 import (
    ConfigActionEnum, ConfigRule,
    Device, DeviceDriverEnum, DeviceId, DeviceList, DeviceOperationalStatusEnum,
Lucie LONG's avatar
Lucie LONG committed
    Empty, TopologyId, ContextId)
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
Lucie LONG's avatar
Lucie LONG committed
from common.tools.context_queries.Device import add_device_to_topology
from context.client.ContextClient import ContextClient
from device.client.DeviceClient import DeviceClient
from webui.service.device.forms import AddDeviceForm
Lucie LONG's avatar
Lucie LONG committed
from common.DeviceTypes import DeviceTypeEnum
Lucie LONG's avatar
Lucie LONG committed
from webui.service.device.forms import ConfigForm
from webui.service.device.forms import UpdateDeviceForm

device = Blueprint('device', __name__, url_prefix='/device')
context_client = ContextClient()
device_client = DeviceClient()

@device.get('/')
def home():
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    if 'context_uuid' not in session or 'topology_uuid' not in session:
        flash("Please select a context!", "warning")
        return redirect(url_for("main.home"))
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed

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

    context_client.connect()
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
    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())
    context_client.close()
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed

    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)

@device.route('add', methods=['GET', 'POST'])
def add():
    form = AddDeviceForm()

    # listing enum values
Lucie LONG's avatar
Lucie LONG committed
    form.operational_status.choices = []
    for key, value in DeviceOperationalStatusEnum.DESCRIPTOR.values_by_name.items():
        form.operational_status.choices.append(
            (DeviceOperationalStatusEnum.Value(key), key.replace('DEVICEOPERATIONALSTATUS_', '')))

Lucie LONG's avatar
Lucie LONG committed
    # items for Device Type field
    for device_type in DeviceTypeEnum:
        form.device_type.choices.append((device_type.value,device_type.value))    

    if form.validate_on_submit():
        device = Device()
Lucie LONG's avatar
Lucie LONG committed
        # Device UUID: 
        device.device_id.device_uuid.uuid = form.device_id.data

Lucie LONG's avatar
Lucie LONG committed
        # Device type: 
        device.device_type = str(form.device_type.data)

        # Device configurations: 
        config_rule = device.device_config.config_rules.add()
        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.device_config.config_rules.add()
        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.device_config.config_rules.add()
        config_rule.action = ConfigActionEnum.CONFIGACTION_SET
        config_rule.custom.resource_key = '_connect/settings'
        config_rule.custom.resource_value = form.device_config_settings.data

        # Device status: 
        device.device_operational_status = form.operational_status.data

Lucie LONG's avatar
Lucie LONG committed
        # Device drivers: 
        if form.device_drivers_undefined.data:
            device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_UNDEFINED)
        if form.device_drivers_openconfig.data:
            device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_OPENCONFIG)
        if form.device_drivers_transport_api.data:
            device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_TRANSPORT_API)
        if form.device_drivers_p4.data:
            device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_P4)
        if form.device_drivers_ietf_network_topology.data:
            device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_IETF_NETWORK_TOPOLOGY)
        if form.device_drivers_onf_tr_352.data:
            device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_ONF_TR_352)
        if form.device_drivers_xr.data:
            device.device_drivers.append(DeviceDriverEnum.DEVICEDRIVER_XR)

        try:
            device_client.connect()
            response: DeviceId = device_client.AddDevice(device)
            device_client.close()
Lucie LONG's avatar
Lucie LONG committed
            context_uuid = session['context_uuid']
            topology_uuid = session['topology_uuid']
            context_client.connect()
            context_id = ContextId(**json_context_id(context_uuid))
            add_device_to_topology(context_client, context_id, topology_uuid, device.device_id.device_uuid.uuid)
            context_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:
            flash(f'Problem adding the device. {e.details()}', 'danger')
        
    return render_template('device/add.html', form=form,
Lucie LONG's avatar
Lucie LONG committed
                        submit_text='Add New Device')
@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)
    context_client.close()
    return render_template('device/detail.html', device=response,
                                                 dde=DeviceDriverEnum,
                                                 dose=DeviceOperationalStatusEnum)
@device.get('<path:device_uuid>/delete')
def delete(device_uuid):
    try:

        # first, check if device exists!
        # request: DeviceId = DeviceId()
        # request.device_uuid.uuid = device_uuid
        # response: Device = client.GetDevice(request)
        # TODO: finalize implementation

        request = DeviceId()
        request.device_uuid.uuid = device_uuid
        device_client.connect()
        response = device_client.DeleteDevice(request)
        device_client.close()

Carlos Natalino's avatar
Carlos Natalino committed
        flash(f'Device "{device_uuid}" deleted successfully!', 'success')
    except Exception as e:
Carlos Natalino's avatar
Carlos Natalino committed
        flash(f'Problem deleting device "{device_uuid}": {e.details()}', 'danger')
Lluis Gifre Renom's avatar
Lluis Gifre Renom committed
        current_app.logger.exception(e)
    return redirect(url_for('device.home'))
Lucie LONG's avatar
Lucie LONG committed

@device.route('<path:device_uuid>/addconfig', methods=['GET', 'POST'])
def addconfig(device_uuid):
    form = ConfigForm()
    request = DeviceId()
    request.device_uuid.uuid = device_uuid
    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()
        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)
            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:
             flash(f'Problem adding the device. {e.details()}', 'danger')

    return render_template('device/addconfig.html', form=form,  submit_text='Add New Configuration')

@device.route('updateconfig', methods=['GET', 'POST'])
def updateconfig():

        
    return render_template('device/updateconfig.html')


@device.route('<path:device_uuid>/update', methods=['GET', 'POST'])
def update(device_uuid):
    form = UpdateDeviceForm()
    request = DeviceId()
    request.device_uuid.uuid = device_uuid
    context_client.connect()
    response = context_client.GetDevice(request)
    context_client.close()

Lucie LONG's avatar
Lucie LONG committed
    # 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_', '')))

    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
        try:
            device_client.connect()
            response: DeviceId = device_client.ConfigureDevice(device)
            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:
             flash(f'Problem updating the device. {e.details()}', 'danger')  
Lucie LONG's avatar
Lucie LONG committed
    return render_template('device/update.html', device=response, form=form, submit_text='Update Device')