diff --git a/my_deploy.sh b/my_deploy.sh index 7dd5e5c3ee13cbce2b701b5b4e703823dfb2c28f..9e4447349c5838fa8cd0cb13429c5fe9f737125c 100755 --- a/my_deploy.sh +++ b/my_deploy.sh @@ -20,7 +20,7 @@ export TFS_REGISTRY_IMAGES="http://localhost:32000/tfs/" # Set the list of components, separated by spaces, you want to build images for, and deploy. -export TFS_COMPONENTS="context device pathcomp service slice nbi webui load_generator" +export TFS_COMPONENTS="context device pathcomp opticalcontroller service slice nbi webui " # Uncomment to activate Monitoring #export TFS_COMPONENTS="${TFS_COMPONENTS} monitoring" diff --git a/src/context/service/ContextServiceServicerImpl.py b/src/context/service/ContextServiceServicerImpl.py index a102fa17629bd866d96883230a542a6e7a4d92ff..1be4cdac8009481c99ea6dd0de6114f4b49e45ce 100644 --- a/src/context/service/ContextServiceServicerImpl.py +++ b/src/context/service/ContextServiceServicerImpl.py @@ -305,7 +305,7 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def GetOpticalConfig(self, request : Empty, context : grpc.ServicerContext) -> OpticalConfigList: result = get_opticalconfig(self.db_engine) - return OpticalConfigList(OpticalConfigs=result) + return OpticalConfigList(opticalconfigs=result) @safe_and_metered_rpc_method(METRICS_POOL, LOGGER) def SetOpticalConfig(self, request : OpticalConfig, context : grpc.ServicerContext) -> OpticalConfigId: @@ -316,5 +316,5 @@ class ContextServiceServicerImpl(ContextServiceServicer, ContextPolicyServiceSer def SelectOpticalConfig(self, request : OpticalConfigId, context : grpc.ServicerContext) -> OpticalConfig: result = select_opticalconfig(self.db_engine, request) optical_config_id = OpticalConfigId() - optical_config_id.CopyFrom(result.OpticalConfig_id) - return OpticalConfig(config=result.config, OpticalConfig_id=optical_config_id) + optical_config_id.CopyFrom(result.opticalconfig_id) + return OpticalConfig(config=result.config, opticalconfig_id=optical_config_id) diff --git a/src/context/service/database/OpticalConfig.py b/src/context/service/database/OpticalConfig.py index 9e7552bc111e40245bb649d2eb1ffa910c6f8588..973e93c3357a3cd32dc3714a1d70c397fbdae8c3 100644 --- a/src/context/service/database/OpticalConfig.py +++ b/src/context/service/database/OpticalConfig.py @@ -18,7 +18,8 @@ from sqlalchemy.engine import Engine from sqlalchemy.orm import Session, sessionmaker from sqlalchemy_cockroachdb import run_transaction from common.proto.context_pb2 import OpticalConfig, OpticalConfigId -from .models.OpticalConfigModel import OpticalConfigModel +from .models.OpticalConfigModel import OpticalConfigModel , OpticalChannelModel +from context.service.database.uuids.OpticalConfig import channel_get_uuid LOGGER = logging.getLogger(__name__) @@ -26,44 +27,61 @@ def get_opticalconfig(db_engine : Engine): def callback(session:Session): optical_configs = list() results = session.query(OpticalConfigModel).all() + for obj in results: + LOGGER.info(f"opticaln config obj from context {obj.dump()}") + optical_config = OpticalConfig() - optical_config.config = json.dump(obj.config) - optical_config.opticalconfig_id.opticalconfig_uuid = obj.opticalconfig_uuid + optical_config.config = json.dumps(obj.dump()) + optical_config.opticalconfig_id.opticalconfig_uuid = obj.dump_id()["opticalconfig_uuid"] optical_configs.append(optical_config) return optical_configs obj = run_transaction(sessionmaker(bind=db_engine), callback) return obj def set_opticalconfig(db_engine : Engine, request : OpticalConfig): + LOGGER.info(f"request {request} ") opticalconfig_id = OpticalConfigId() opticalconfig_id.opticalconfig_uuid = request.opticalconfig_id.opticalconfig_uuid - my_config_data = [] + OpticalConfig_data = [] if request.config: channels = [] transceivers = [] config = json.loads(request.config) - if 'channels' in config and len(config['channels']) > 0: - channels = [channel['name']['index'] for channel in config['channels']] + if 'transceivers' in config and len(config['transceivers']['transceiver']) > 0: transceivers = [transceiver for transceiver in config['transceivers']['transceiver']] - - my_config_data = [ - { - "opticalconfig_uuid": request.opticalconfig_id.opticalconfig_uuid, - "channels" : channels, - "transcievers" : transceivers, - "interfaces" : json.dumps(config["interfaces"]["interface"]), - "channel_namespace" : config["channel_namespace"], - "endpoints" : [json.dumps(endpoint) for endpoint in config["endpoints"]], - "frequency" : config["frequency"] if "frequency" in config else 0, - "operational_mode" : config["operational_mode"] if "operational_mode" in config else 0, - "output_power" : config["output_power"] if "output_power" in config else '', - } - ] - + + if 'channels' in config and len(config['channels']) > 0: + #channels = [channel['name']['index'] for channel in config['channels']] + for channel_params in config['channels']: + channels.append( + { + "channel_uuid":channel_get_uuid(channel_params['name']['index']), + "opticalconfig_uuid": request.opticalconfig_id.opticalconfig_uuid, + "channel_name" : channel_params['name']['index'], + "frequency" : int(channel_params["frequency"]) if "frequency" in channel_params else 0, + "operational_mode" : int(channel_params["operational-mode"]) if "operational-mode" in channel_params else 0, + "target_output_power" : channel_params["target-output-power"] if "target-output-power" in channel_params else '', + } + ) + + OpticalConfig_data.append( + { + "opticalconfig_uuid": request.opticalconfig_id.opticalconfig_uuid, + "transcievers" : transceivers, + "interfaces" : json.dumps(config["interfaces"]["interface"]), + "channel_namespace" : config["channel_namespace"], + "endpoints" : [json.dumps(endpoint) for endpoint in config["endpoints"]],} + + ) + + + LOGGER.info(f"optical config to set {OpticalConfig_data} ") + LOGGER.info(f"channels {channels}") def callback(session:Session)->bool: - stmt = insert(OpticalConfigModel).values(my_config_data) + stmt = insert(OpticalConfigModel).values(OpticalConfig_data) + stmt = stmt.on_conflict_do_update( index_elements=[OpticalConfigModel.opticalconfig_uuid], set_=dict( @@ -71,7 +89,17 @@ def set_opticalconfig(db_engine : Engine, request : OpticalConfig): ) ) stmt = stmt.returning(OpticalConfigModel.opticalconfig_uuid) - id = session.execute(stmt).fetchone() + opticalconfig_id = session.execute(stmt).fetchone() + if (len(channels)>0) : + + stmt = insert(OpticalChannelModel).values(channels) + + stmt = stmt.on_conflict_do_nothing( + index_elements=[OpticalChannelModel.channel_uuid , OpticalConfigModel.opticalconfig_uuid], + + ) + stmt = stmt.returning(OpticalChannelModel.channel_uuid) + opticalChannel_id = session.execute(stmt).fetchone() opticalconfig_id = run_transaction(sessionmaker(bind=db_engine), callback) return {'opticalconfig_uuid': opticalconfig_id} diff --git a/src/context/service/database/models/OpticalConfigModel.py b/src/context/service/database/models/OpticalConfigModel.py index 10cf197f9a8a728b8fd02bdcdaf255677551bf17..8531f37b270af80696ecdf22675e5aba42d6d0ee 100644 --- a/src/context/service/database/models/OpticalConfigModel.py +++ b/src/context/service/database/models/OpticalConfigModel.py @@ -13,30 +13,58 @@ # limitations under the License. import json -from sqlalchemy import Column, String, Integer +from sqlalchemy import Column, String, Integer , ForeignKey from sqlalchemy.dialects.postgresql import ARRAY +from sqlalchemy.orm import relationship from ._Base import _Base class OpticalConfigModel(_Base): __tablename__ = 'optical_config' opticalconfig_uuid = Column(String, primary_key=True) - channels = Column(ARRAY(String), nullable=True) + transcievers = Column(ARRAY(String), nullable=True) interfaces = Column(String, nullable=True) channel_namespace = Column(String, nullable=True) endpoints = Column(ARRAY(String), nullable=True) - frequency = Column(Integer, nullable=True) - operational_mode = Column(Integer, nullable=True) - output_power = Column(String, nullable=True) + channels = relationship("OpticalChannelModel") + + + def dump_id (self ): + return { + "opticalconfig_uuid":self.opticalconfig_uuid + } def dump(self): return { - "channels" : [{'name': {'index': channel}} for channel in self.channels], + "channels" : [channel.dump() for channel in self.channels], "transceivers" : {"transceiver": [transciever for transciever in self.transcievers]}, "interfaces" : {"interface": json.loads(self.interfaces)}, "channel_namespace" : self.channel_namespace, "endpoints" : [json.loads(endpoint) for endpoint in self.endpoints], + + } + + + +class OpticalChannelModel(_Base): + __tablename__ = 'optical_channel' + channel_uuid = Column(String, primary_key=True) + channel_name = Column (String,nullable=True) + frequency = Column(Integer, nullable=True) + operational_mode = Column(Integer, nullable=True) + target_output_power = Column(String, nullable=True) + opticalconfig_uuid = Column(ForeignKey('optical_config.opticalconfig_uuid', ondelete='CASCADE' ), primary_key=True) + opticalconfig = relationship('OpticalConfigModel', back_populates='channels') + def dump_id (self ): + return { + "channel_uuid":self.channel_uuid + } + + def dump(self): + return { + "name" :{'index':self.channel_name}, "frequency" : self.frequency, - "output_power" : self.output_power, + "target_output_power" : self.target_output_power, "operational_mode" : self.operational_mode, } + diff --git a/src/context/service/database/models/OpticalLinkModel.py b/src/context/service/database/models/OpticalLinkModel.py new file mode 100644 index 0000000000000000000000000000000000000000..b94eeda932faae1e546be72bc20649add3af7a7a --- /dev/null +++ b/src/context/service/database/models/OpticalLinkModel.py @@ -0,0 +1,78 @@ + +import operator +from sqlalchemy import CheckConstraint, Column, DateTime, Float, ForeignKey, Integer, String ,Boolean +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.types import ARRAY +from sqlalchemy.orm import relationship +from typing import Dict +from ._Base import _Base + +class OpticalLinkModel(_Base): + __tablename__ = 'opticallink' + + optical_link_uuid = Column(UUID(as_uuid=False), primary_key=True) + optical_link_name = Column(String, nullable=False) + length = Column(Integer, nullable=True) + source = Column(String, nullable=True) + target = Column(String, nullable=True) + optical_link_fiber= relationship("FiberModel") + created_at = Column(DateTime, nullable=False) + updated_at = Column(DateTime, nullable=False) + + + + + def dump_id(self) -> Dict: + return {'optical_link_uuid': {'uuid': self.link_uuid}} + + def dump(self) -> Dict: + result = { + 'optical_link_id' : self.dump_id(), + 'name' : self.optical_link_name, + 'details': { + "length" : self.length, + 'source' : self.source, + "target" : self.target, + 'fibers' : [ fiber.dump() for fiber in self.optical_link_fiber ] + } + + } + + return result + +class FiberModel(_Base): + __tablename__ = 'fiber' + fiber_uuid = Column(UUID(as_uuid=False), primary_key=True) + fiber_length = Column(Integer, nullable=True) + source_port = Column(String, nullable=True) + destination_port = Column(String, nullable=True) + local_peer_port = Column(String, nullable=True) + remote_peer_port = Column(String, nullable=True) + used = Column(Boolean ,nullable=true) + c_slots = Column (ARRAY(Integer),nullable=True) + l_slots = Column (ARRAY(Integer),nullable=True) + s_slots = Column (ARRAY(Integer),nullable=True) + optical_link_uuid = Column(ForeignKey('opticallink.optical_link_uuid', ondelete='CASCADE' ), primary_key=True) + optical_link = relationship('OpticalLinkModel', back_populates='optical_link_fibers') + + + def dump_id(self) -> Dict: + return {'fiber_uuid': {'uuid': self.fiber_uuid}} + + + def dump(self) -> Dict: + result = { + 'ID' : self.dump_id(), + 'length' : self.fiber_length, + "src_port" : self.source_port, + "dst_port" : self.destination_port, + "local_peer_port" : self.local_peer_port, + "remote_peer_port" : self.remote_peer_port, + "used" : self.used, + "c_slots" : self.c_slots , + "l_slots" : self.l_slots, + "s_slots" : self.s_slots + + } + + return result \ No newline at end of file diff --git a/src/context/service/database/uuids/OpticalConfig.py b/src/context/service/database/uuids/OpticalConfig.py new file mode 100644 index 0000000000000000000000000000000000000000..0003b5712c917f046f942e40a638dd1c95e3d62d --- /dev/null +++ b/src/context/service/database/uuids/OpticalConfig.py @@ -0,0 +1,17 @@ + +from common.method_wrappers.ServiceExceptions import InvalidArgumentsException +from ._Builder import get_uuid_from_string, get_uuid_random + +def channel_get_uuid( + channel_name :str , allow_random : bool = False +) -> str: + + + if len(channel_name) > 0: + return get_uuid_from_string(channel_name) + if allow_random: return get_uuid_random() + + raise InvalidArgumentsException([ + ('channel uuid', channel_name), + + ], extra_details=['Channel name is required to produce a channel UUID']) diff --git a/src/device/service/drivers/oc_driver/OCDriver.py b/src/device/service/drivers/oc_driver/OCDriver.py index 16f00cfb4c7b80d807882ab5b4c95b36b28db8f6..4b49c7e4122d5c625666cef3a9eb77a56d19c273 100644 --- a/src/device/service/drivers/oc_driver/OCDriver.py +++ b/src/device/service/drivers/oc_driver/OCDriver.py @@ -257,7 +257,8 @@ class OCDriver(_Driver): xml_data = self.__netconf_handler.get().data_xml transceivers,interfaces,channels_lst,channel_namespace,endpoints=extractor(data_xml=xml_data,resource_keys=filter_fields,dic=config) - + logging.info(f"xml response {xml_data}") + except Exception as e: # pylint: disable=broad-except MSG = 'Exception retrieving {:s}' self.__logger.info("error from getConfig %s",e) @@ -273,7 +274,7 @@ class OCDriver(_Driver): value_dic["interfaces"]=interfaces value_dic["channel_namespace"]=channel_namespace value_dic["endpoints"]=endpoints - + logging.info(f"parameters {value_dic}") opticalConfig.config=json.dumps(value_dic) opticalConfig.opticalconfig_id.opticalconfig_uuid=self.__device_uuid if self.__device_uuid is not None else "" config_id=context_client.SetOpticalConfig(opticalConfig) diff --git a/src/device/service/drivers/oc_driver/templates/Tools.py b/src/device/service/drivers/oc_driver/templates/Tools.py index 909bdd83bf4c189da0a778268ce40119cca7c452..bc69db6a6e6afe2d1fe4d5b2d30f6471db5fa303 100644 --- a/src/device/service/drivers/oc_driver/templates/Tools.py +++ b/src/device/service/drivers/oc_driver/templates/Tools.py @@ -140,9 +140,10 @@ def extract_channels_based_on_type (xml_data:str): return channel_names def extract_value(resource_key:str,xml_data:str,dic:dict,channel_name:str,channel_namespace:str): + logging.info(f"resource_key {resource_key} and channgel_name {channel_name} and channel_namespace {channel_namespace}") xml_bytes = xml_data.encode("utf-8") root = ET.fromstring(xml_bytes) - + channel_name=channel_name if 'index' not in channel_name else channel_name['index'] namespace = {'oc': 'http://openconfig.net/yang/platform', 'td': channel_namespace} @@ -153,10 +154,13 @@ def extract_value(resource_key:str,xml_data:str,dic:dict,channel_name:str,channe if (parameter is not None): value = parameter.text dic[resource_key]=value + else : + logging.info("parameter is None") else: + logging.info("element is None") print(" element not found.") - + logging.info(f"dic {dic}") return dic @@ -244,10 +248,11 @@ def extractor(data_xml:str,resource_keys:list,dic:dict): for channel_name in channel_names: dic={} - for resource_key in resource_keys : + for resource_key in resource_keys : if (resource_key != 'interface'): dic=extract_value(dic=dic,resource_key=resource_key,xml_data=data_xml,channel_name=channel_name,channel_namespace=channel_namespace) + dic["name"]=channel_name endpoints.append({"endpoint_uuid":{"uuid":channel_name}}) lst_dic.append(dic) diff --git a/src/tests/ofc24/run_test.sh b/src/tests/ofc24/run_test.sh new file mode 100644 index 0000000000000000000000000000000000000000..660436552cebca50c7e628478ab4e66beed6f08d --- /dev/null +++ b/src/tests/ofc24/run_test.sh @@ -0,0 +1,17 @@ +#!/bin/bash +docker stop na1 +docker rm na1 + screen -dmS t1 -T xterm sh -c "docker run -p 10.0.2.10:2023:2022 -v ~/tfs-ctrl/tempOC/files:/files --name na1 -it asgamb1/oc23bgp.img:latest +" + sleep 2 + if [ "$( docker container inspect -f '{{.State.Running}}' na1)" = "true" ]; then + + + + docker exec na1 sh -c " cp /files/platform_t1.xml demoECOC21.xml ; + + /confd/examples.confd/OC23/startNetconfAgent.sh; " + else + echo "your container is not running yet" + fi + diff --git a/src/webui/service/__init__.py b/src/webui/service/__init__.py index 63192016cbb34e53332d1af2ab455bfea5b45a78..196652d3fb6f9648ec2dde9b4c38792fd05e3f9d 100644 --- a/src/webui/service/__init__.py +++ b/src/webui/service/__init__.py @@ -83,6 +83,9 @@ def create_app(use_config=None, web_app_root=None): from webui.service.load_gen.routes import load_gen # pylint: disable=import-outside-toplevel app.register_blueprint(load_gen) + + from webui.service.opticalconfig.routes import opticalconfig # pylint: disable=import-outside-toplevel + app.register_blueprint(opticalconfig) from webui.service.service.routes import service # pylint: disable=import-outside-toplevel app.register_blueprint(service) diff --git a/src/webui/service/opticalconfig/__init__.py b/src/webui/service/opticalconfig/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1549d9811aa5d1c193a44ad45d0d7773236c0612 --- /dev/null +++ b/src/webui/service/opticalconfig/__init__.py @@ -0,0 +1,14 @@ +# 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. + diff --git a/src/webui/service/opticalconfig/forms.py b/src/webui/service/opticalconfig/forms.py new file mode 100644 index 0000000000000000000000000000000000000000..30c1332790fa92bcf936035c0e5a8a666f878fe0 --- /dev/null +++ b/src/webui/service/opticalconfig/forms.py @@ -0,0 +1,22 @@ + + +from flask_wtf import FlaskForm +from wtforms import StringField, SelectField, TextAreaField, SubmitField +from wtforms.validators import DataRequired, Length, NumberRange, ValidationError +from common.proto.context_pb2 import DeviceOperationalStatusEnum + +class UpdateDeviceForm(FlaskForm): + power = StringField('Power') + frequency= StringField("Frequency") + operational_mode=StringField("Operational Mode") + line_port=SelectField("Line Port") + + + submit = SubmitField('Update') + +class AddTrancseiver (FlaskForm): + transceiver = StringField("Transceiver") + submit = SubmitField('Add') +class UpdateInterfaceForm (FlaskForm): + ip=StringField("IP Address") + prefix_length=StringField("Prefix Length") \ No newline at end of file diff --git a/src/webui/service/opticalconfig/routes.py b/src/webui/service/opticalconfig/routes.py new file mode 100644 index 0000000000000000000000000000000000000000..5445d140e587418b8df9ce819756fb92d6ff16ea --- /dev/null +++ b/src/webui/service/opticalconfig/routes.py @@ -0,0 +1,193 @@ +import base64, json, logging #, re +from flask import jsonify, redirect, render_template, Blueprint, flash, session, url_for, request +from common.proto.context_pb2 import (ContextList, Empty, TopologyId, TopologyList +,DeviceId,DeviceList ,OpticalConfig, OpticalConfigId ,OpticalConfigList) +from common.tools.descriptor.Loader import DescriptorLoader, compose_notifications +from common.tools.grpc.Tools import grpc_message_to_json_string +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 +from service.client.ServiceClient import ServiceClient +from slice.client.SliceClient import SliceClient +from webui.service.main.forms import ContextTopologyForm, DescriptorForm +from .forms import UpdateDeviceForm ,AddTrancseiver ,UpdateInterfaceForm + + + +opticalconfig = Blueprint('opticalconfig', __name__,url_prefix="/opticalconfig") + +context_client = ContextClient() +device_client = DeviceClient() +service_client = ServiceClient() +slice_client = SliceClient() + +LOGGER = logging.getLogger(__name__) + +DESCRIPTOR_LOADER_NUM_WORKERS = 10 + +@opticalconfig.get("/") +def home() : + config=[] + deviceId= DeviceId() + + 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'] + topology_uuid = session['topology_uuid'] + + context_client.connect() + opticalConfig_list:OpticalConfigList = context_client.GetOpticalConfig(Empty()) + logging.info("myconfigList %s",opticalConfig_list) + + for configs in opticalConfig_list.opticalconfigs: + + value=json.loads(configs.config) if type(configs.config)==str else configs.config + value["channels_number"]=len(value['channels']) + + # value['operationalMode']=value['operational-mode'] + # value['targetOutputPower']=value['target-output-power'] + value['opticalconfig_id']=configs.opticalconfig_id + # value['line_port']=value["line-port"] + + config.append(value) + + logging.info("opticalConfig %s",config) + + context_client.close() + + return render_template( + 'opticalconfig/home.html', config=config) +@opticalconfig.route('<path:config_uuid>/detail',methods=['GET']) +def show_details(config_uuid): + opticalconfigId=OpticalConfigId() + opticalconfigId.opticalconfig_uuid=config_uuid + device_details=[] + context_client.connect() + response = context_client.SelectOpticalConfig(opticalconfigId) + context_client.close() + LOGGER.info("response %s",response) + opticalConfig = OpticalConfig() + opticalConfig.CopyFrom(response) + + config =json.loads(opticalConfig.config) + LOGGER.info("config details %s",config) + interfaces=config["interfaces"] + new_config={} + + for channel in config['channels'] : + + new_config["name"]=channel['name'] + new_config['operationalMode']=channel['operational_mode'] if 'operational_mode' in channel else '' + new_config['targetOutputPower']=channel['target_output_power'] if 'target_output_power' in channel else '' + new_config["frequency"]=channel['frequency'] if 'frequency' in channel else '' + new_config['line_port']=channel["line-port"] if 'line-port' in channel else '' + + device_details.append(new_config) + LOGGER.info("config details %s",device_details) + return render_template('opticalconfig/details.html', device=device_details,config_id=config_uuid,interfaces=interfaces) +@opticalconfig.route('<path:config_uuid>/<path:channel_name>/update', methods=['GET', 'POST']) +def update(config_uuid,channel_name): + form = UpdateDeviceForm() + + opticalconfigId=OpticalConfigId() + opticalconfigId.opticalconfig_uuid=config_uuid + context_client.connect() + response = context_client.SelectOpticalConfig(opticalconfigId) + context_client.close() + LOGGER.info("response %s",response) + opticalconfig = OpticalConfig() + opticalconfig.CopyFrom(response) + config =json.loads(opticalconfig.config) + new_config={} + for channel in config['channels']: + if (channel["name"] == channel_name): + new_config=channel + form.frequency.default = channel["frequency"] + form.operational_mode.default=channel["operational-mode"] + form.power.default=channel["target-output-power"] + form.line_port.choices = [("","")] + + for transceiver in config["transceivers"]['transceiver']: + + form.line_port.choices.append((transceiver,transceiver)) + # listing enum values + + if form.validate_on_submit(): + + new_config["target-output-power"] =form.power.data if form.power.data != '' else new_config['target-output-power'] + new_config["frequency"]=form.frequency.data if form.frequency.data != '' else new_config['frequency'] + new_config["operational-mode"]=form.operational_mode.data if form.operational_mode.data != '' else new_config['operational-mode'] + new_config["line-port"]=form.line_port.data if form.line_port.data != '' else new_config['line-port'] + + opticalconfig.config =json.dumps(new_config) + LOGGER.info("myconfig copied %s",opticalconfig) + try: + device_client.connect() + device_client.ConfigureOpticalDevice(opticalconfig) + device_client.close() + flash(f' device was updated.', 'success') + return redirect(url_for('opticalconfig.show_details',config_uuid=config_uuid)) + except Exception as e: # pylint: disable=broad-except + flash(f'Problem updating the device. {e}', 'danger') + return render_template('myabout/update.html', device=response, form=form, submit_text='Update Device',channel_name=channel_name) +@opticalconfig.route('<path:config_uuid>/<path:interface_name>/update_interface', methods=['GET', 'POST']) +def update_interface (config_uuid,interface_name): + form = UpdateInterfaceForm() + opticalconfigId=OpticalConfigId() + opticalconfigId.opticalconfig_uuid=config_uuid + context_client.connect() + response = context_client.SelectOpticalConfig(myid) + context_client.close() + LOGGER.info("response %s",response) + opticalconfig = OpticalConfig() + opticalconfig.CopyFrom(response) + config =json.loads(opticalconfig.config) + new_config={} + if form.validate_on_submit(): + new_config["ip"]=form.ip.data if form.ip.data != "" else config["interfaces"]["interface"]["ip"] + new_config["prefix-length"]=form.prefix_length.data if form.prefix_length.data != "" else config["interfaces"]["interface"]["prefix-length"] + new_config["name"]=config["interfaces"]["interface"]["name"] + new_config["enabled"]=config["interfaces"]["interface"]["enabled"] + + opticalconfig.config=json.dumps({"update_interface":new_config}) + try: + device_client.connect() + device_client.ConfigureOpticalDevice(opticalconfig) + device_client.close() + flash(f' device was updated.', 'success') + return redirect(url_for('opticalconfig.show_details',config_uuid=config_uuid)) + except Exception as e: # pylint: disable=broad-except + flash(f'Problem updating the device. {e}', 'danger') + return render_template('opticalconfig/update_interface.html', + device=response, form=form, submit_text='Update interface',interface_name=interface_name) + +@opticalconfig.route('<path:config_uuid>/add_transceiver', methods=['GET','POST']) +def add_transceiver (config_uuid): + config={} + addtrancseiver=AddTrancseiver() + opticalconfigId=OpticalConfig() + opticalconfigId.opticalconfig_uuid=config_uuid + context_client.connect() + response = context_client.SelectOpticalConfig(opticalconfigId) + context_client.close() + opticlConfig=OpticalConfig() + opticlConfig.CopyFrom(response) + if addtrancseiver.validate_on_submit(): + config["add_transceiver"]=addtrancseiver.transceiver.data + opticlConfig.config=json.dumps(config) + + try: + device_client.connect() + device_client.ConfigureOpticalDevice(opticlConfig) + device_client.close() + flash(f' device was updated.', 'success') + return redirect(url_for('opticalconfig.update',config_uuid=config_uuid)) + except Exception as e: # pylint: disable=broad-except + flash(f'Problem updating the device. {e}', 'danger') + return render_template('opticalconfig/add_transceiver.html',form=addtrancseiver, submit_text='Add Trancseiver') + + + + \ No newline at end of file diff --git a/src/webui/service/templates/base.html b/src/webui/service/templates/base.html index 60cd5aebde39019b34c6ff2b763137f5c3d05035..e081b2710368dbe81c9ba432d0597fbe0240c2dd 100644 --- a/src/webui/service/templates/base.html +++ b/src/webui/service/templates/base.html @@ -83,6 +83,13 @@ <a class="nav-link" href="{{ url_for('slice.home') }}">Slice</a> {% endif %} </li> + <li class="nav-item"> + {% if '/opticalconfig/' in request.path %} + <a class="nav-link active" aria-current="page" href="{{ url_for('opticalconfig.home') }}">Optical Config</a> + {% else %} + <a class="nav-link" href="{{ url_for('opticalconfig.home') }}">Optical Config</a> + {% endif %} + </li> <li class="nav-item"> {% if '/policy_rule/' in request.path %} <a class="nav-link active" aria-current="page" href="{{ url_for('policy_rule.home') }}">Policy Rules</a> diff --git a/src/webui/service/templates/opticalconfig/add_transceiver.html b/src/webui/service/templates/opticalconfig/add_transceiver.html new file mode 100644 index 0000000000000000000000000000000000000000..836bf7d5d0546f5671e65b97db93936e9ce4455d --- /dev/null +++ b/src/webui/service/templates/opticalconfig/add_transceiver.html @@ -0,0 +1,40 @@ + +{% extends 'base.html' %} + +{% block content %} +<h1>Add Transceiver </h1> +<br /> +<form id="Add_transceiver" method="POST"> + {{ form.hidden_tag() }} + <fieldset> + <div class="row mb-3 "> + <div class="col-12"> + {{ form.transceiver.label(class="col-sm-2 col-form-label") }} + <div class="col-sm-10"> + {% if form.transceiver.errors %} + {{ form.transceiver(class="form-control is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.transceiver.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + + {{ form.transceiver(class="col-sm-7 form-control") }} + + {% endif %} + </div> + + </div> + <button type="submit" class="btn btn-primary"> + <i class="bi bi-plus-circle-fill"></i> + {{ submit_text }} + </button> + <button type="button" class="btn btn-block btn-secondary" onclick="javascript: history.back()"> + <i class="bi bi-box-arrow-in-left"></i> + Cancel + </button> + </div> + </fieldset> +</form> +{% endblock %} \ No newline at end of file diff --git a/src/webui/service/templates/opticalconfig/details.html b/src/webui/service/templates/opticalconfig/details.html new file mode 100644 index 0000000000000000000000000000000000000000..922f61c674bbd64a03247c06947c75dc46e0e19f --- /dev/null +++ b/src/webui/service/templates/opticalconfig/details.html @@ -0,0 +1,109 @@ +{% extends 'base.html' %} + +{% block content %} +<h1>My Configurations</h1> + +<div class="row"> + {% if device %} + <div class="col-sm-12"> + <span>Device ID:</span> + <h5>{{config_id}}</h5> + </div> + <div class="col-sm-12"> + <div class="col-sm-12"> + <span>Interface:</span> + <span> + <ul> + <li><span>Name:</span><span class="font-weight-bold" style="font-weight: 700;">{{ interfaces.interface.name + }}</span> </li> + <li><span>Ip:</span> <span class="font-weight-bold" style="font-weight: 700;">{{ interfaces.interface.ip }}</span> + <li><span>Ip:</span> <span class="font-weight-bold" style="font-weight: 700;">{{ interfaces.interface["prefix-legnth"] }}</span> + </li> + <li><span>Enabled:</span> <span class="font-weight-bold" style="font-weight: 700;"> {{ interfaces.interface.enabled + }}</span></li> + + + </ul> + </span> + </div> + <div class="col-sm-12"> + + <div class="col-sm-3"> + <a id="update" class="btn btn-secondary" + href="{{ url_for('opticalconfig.update_interface',config_uuid=config_id,interface_name=interfaces.interface.name) }}"> + <i class="bi bi-pencil-square"></i> + Update interface + </a> + </div> + + <div class="col-sm-12 hr bg-primary" style="height:1px; margin:5px 0;"></div> + </div> + </div> + {% for channel in device %} + <div class="col-sm-12"> + <span style="font-weight: 700;">channel name:{{channel.name.index}}</span> + </div> + + + <div class="col-sm-4"> + <span>Frequency:</span> + <p class="font-weight-bold" style="font-weight: 700;">{{channel.frequency}}</p> + </div> + <div class="col-sm-4"> + <span>Target Output Power:</span> + <span class="font-weight-bold" style="font-weight: 700;">{{channel.targetOutputPower}}</span> + </div> + <div class="col-sm-4"> + <span>Operational Mode:</span> + <span class="font-weight-bold" style="font-weight: 700;">{{channel.operationalMode}}</span> + </div> + <div class="col-sm-4"> + <span>Line Port:</span> + <span class="font-weight-bold" style="font-weight: 700;">{{channel.line_port}}</span> + </div> + + <div class="col-sm-12"> + + <div class="col-sm-3"> + <a id="update" class="btn btn-secondary" + href="{{ url_for('opticalconfig.update',config_uuid=config_id,channel_name=channel.name) }}"> + <i class="bi bi-pencil-square"></i> + Update channel + </a> + </div> + </div> + + <div class="col-sm-12 hr bg-primary" style="height:1px; margin:5px 0;"></div> + {% endfor %} + +</div> + +{% else %} +<div class="col"> + <h4 colspan="7">No devices found</h4> +</div> +{% endif %} +<!-- <div class="col"> + <a href="{{ url_for('service.add') }}" class="btn btn-primary" style="margin-bottom: 10px;"> + <i class="bi bi-plus"></i> + Add New Service + </a> + </div> --> + +<!-- Only display XR service addition button if there are XR constellations. Otherwise it might confuse + user, as other service types do not have GUI to add service yet. --> + +<!-- <div class="col"> + <form> + <div class="input-group"> + <input type="text" aria-label="Search" placeholder="Search..." class="form-control"/> + <button type="submit" class="btn btn-primary">Search</button> + </div> + </form> + </div> --> +</div> + + + + +{% endblock %} \ No newline at end of file diff --git a/src/webui/service/templates/opticalconfig/home.html b/src/webui/service/templates/opticalconfig/home.html new file mode 100644 index 0000000000000000000000000000000000000000..2e0a5da67c73c38f6d12664edc2da6b0be66f8cf --- /dev/null +++ b/src/webui/service/templates/opticalconfig/home.html @@ -0,0 +1,68 @@ +{% extends 'base.html' %} + +{% block content %} + <h1>My Configurations</h1> + + <div class="row"> + {% if config %} + + <table class="table table-striped table-hover"> + <thead> + <tr> + <th scope="col">UUID</th> + <th scope="col">Channels Number</th> + + </tr> + </thead> + <tbody> + + {% for device in config %} + <tr> + <td>{{device.opticalconfig_id.opticalconfig_uuid}}</td> + <td>{{ device.channels_number }}</td> + + <td> + <a href="{{ url_for('opticalconfig.show_details', config_uuid=device.opticalconfig_id.opticalconfig_uuid) }}"> + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-eye" viewBox="0 0 16 16"> + <path d="M16 8s-3-5.5-8-5.5S0 8 0 8s3 5.5 8 5.5S16 8 16 8zM1.173 8a13.133 13.133 0 0 1 1.66-2.043C4.12 4.668 5.88 3.5 8 3.5c2.12 0 3.879 1.168 5.168 2.457A13.133 13.133 0 0 1 14.828 8c-.058.087-.122.183-.195.288-.335.48-.83 1.12-1.465 1.755C11.879 11.332 10.119 12.5 8 12.5c-2.12 0-3.879-1.168-5.168-2.457A13.134 13.134 0 0 1 1.172 8z"/> + <path d="M8 5.5a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zM4.5 8a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"/> + </svg> + </a> + </td> + </tr> + {% endfor %} + + </tbody> + </table> + + </div> + + {% else %} + <div class="col"> + <h4 colspan="7">No devices found</h4> + </div> + {% endif %} + <!-- <div class="col"> + <a href="{{ url_for('service.add') }}" class="btn btn-primary" style="margin-bottom: 10px;"> + <i class="bi bi-plus"></i> + Add New Service + </a> + </div> --> + + <!-- Only display XR service addition button if there are XR constellations. Otherwise it might confuse + user, as other service types do not have GUI to add service yet. --> + + <!-- <div class="col"> + <form> + <div class="input-group"> + <input type="text" aria-label="Search" placeholder="Search..." class="form-control"/> + <button type="submit" class="btn btn-primary">Search</button> + </div> + </form> + </div> --> + </div> + + + + +{% endblock %} \ No newline at end of file diff --git a/src/webui/service/templates/opticalconfig/update_interface.html b/src/webui/service/templates/opticalconfig/update_interface.html new file mode 100644 index 0000000000000000000000000000000000000000..38709ac4752355f19a30505f72ae0fa78be3311f --- /dev/null +++ b/src/webui/service/templates/opticalconfig/update_interface.html @@ -0,0 +1,73 @@ +{% extends 'base.html' %} + +{% block content %} +<div class="col-sm-12 d-flex flex-column"> + + <h1>Update Device {{ device.opticalconfig_id.opticalconfig_uuid }}</h1> + <h2> interface :{{interface_name}}</h2> +</div> +<br /> +<form id="update_device" method="POST"> + {{ form.hidden_tag() }} + <fieldset> + <div class="row mb-3 "> + <div class="col-12"> + {{ form.ip.label(class="col-sm-2 col-form-label") }} + <div class="col-sm-10"> + {% if form.ip.errors %} + {{ form.ip(class="form-control is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.ip.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + + {{ form.ip(class="col-sm-7 form-control") }} + + {% endif %} + </div> + </div> + <div class="col-12 "> + {{ form.prefix_length.label(class="col-sm-2 col-form-label") }} + <div class="col-sm-10"> + {% if form.prefix_length.errors %} + {{ form.prefix_length(class="form-control is-invalid") }} + <div class="invalid-feedback"> + {% for error in form.prefix_length.errors %} + <span>{{ error }}</span> + {% endfor %} + </div> + {% else %} + + + {{ form.prefix_length(class="col-sm-7 form-control") }} + + {% endif %} + </div> + + + </div> + </div> + <div class="col-sm-12"> + <div class="row"> + <div class="col-sm-3"> + + <button type="submit" class="btn btn-primary"> + <i class="bi bi-plus-circle-fill"></i> + update interface + </button> + </div> + <div class="col-sm-3 mx-1"> + + <button type="button" class="btn btn-block btn-secondary" onclick="javascript: history.back()"> + <i class="bi bi-box-arrow-in-left"></i> + Cancel + </button> + </div> + </div> + </div> + </div> + </fieldset> +</form> +{% endblock %} \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000000000000000000000000000000000000..d27e15d38062682cfc0e9103fa087bec13328fc5 --- /dev/null +++ b/test.py @@ -0,0 +1,41 @@ +from ncclient import manager +from ncclient.xml_ import * +import lxml.etree as ET + +device = { + 'host': '10.0.2.10', # IP address or hostname of the remote machine + 'port': 2023, # SSH port (default: 22) + 'username': 'admin', # SSH username + 'password': 'admin', # SSH password + 'device_params': {'name': 'default'}, + 'hostkey_verify':False, + "allow_agent":False + ,"look_for_keys":False +} +def extract_value (xml_data): + xml_bytes = xml_data.encode("utf-8") + root = ET.fromstring(xml_bytes) + namespace = {'oc': 'http://openconfig.net/yang/platform', + 'td': 'http://openconfig.net/yang/terminal-device'} + + element = root.find('.//oc:component[oc:name="channel-4"]', namespace) + if element is not None: + parameter= element.find('.//td:frequency',namespace) + if (parameter is not None): + print(parameter.text) + else : + print("parameter is None") + + else: + print(" element not found.") + +with manager.connect(**device) as m: + # Perform operations on the remote machine using the 'm' object + # For example, you can retrieve the running configuration: + #result =m.edit_config(target='running',config=edit_config) + running_config = m.get_config('running').data_xml + + + + extract_value(running_config) +