Loading src/nbi/service/rest_server/nbi_plugins/ietf_hardware/HardwareMultipleDevices.py 0 → 100644 +50 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. import logging from flask import request from flask.json import jsonify from flask_restful import Resource from common.proto.context_pb2 import Empty from context.client.ContextClient import ContextClient from ..tools.Authentication import HTTP_AUTH from ..tools.HttpStatusCodes import HTTP_OK, HTTP_SERVERERROR from .YangHandler import YangHandler LOGGER = logging.getLogger(__name__) class HardwareMultipleDevices(Resource): @HTTP_AUTH.login_required def get(self): LOGGER.debug('Request: {:s}'.format(str(request))) try: context_client = ContextClient() list_devices = context_client.ListDevices(Empty()) LOGGER.info('Request: {:s}'.format(str(list_devices))) hardware_list_reply = [] yang_handler = YangHandler() for device in list_devices.devices: hardware_reply = yang_handler.compose(device) hardware_list_reply.append(hardware_reply) yang_handler.destroy() response = jsonify(hardware_list_reply) response.status_code = HTTP_OK except Exception as e: # pylint: disable=broad-except MSG = 'Something went wrong Retrieving Hardware of Devices({:s})' response = jsonify({'error': str(e)}) response.status_code = HTTP_SERVERERROR return response src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py +48 −49 Original line number Diff line number Diff line Loading @@ -12,19 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. import libyang, os from common.proto.context_pb2 import Device from typing import Dict, Optional import datetime import json import logging import libyang import os import re import datetime LOGGER = logging.getLogger(__name__) YANG_DIR = os.path.join(os.path.dirname(__file__), 'yang') YANG_MODULES = [ 'iana-hardware', 'ietf-hardware' 'ietf-hardware', 'ietf-network-hardware-inventory' ] class YangHandler: Loading @@ -35,7 +37,7 @@ class YangHandler: self._yang_context.load_module(yang_module_name).feature_enable_all() def parse_to_dict(self, message : Dict) -> Dict: yang_module = self._yang_context.get_module('ietf-hardware') yang_module = self._yang_context.get_module('ietf-network-hardware-inventory') dnode : Optional[libyang.DNode] = yang_module.parse_data_dict( message, validate_present=True, validate=True, strict=True ) Loading @@ -44,66 +46,54 @@ class YangHandler: dnode.free() return message @staticmethod def convert_to_iso_date(date_str: str) -> Optional[str]: date_str = date_str.strip('"') # Define the regex pattern for ISO 8601 date format pattern = r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-]\d{2}:\d{2})" # Check if the input date string matches the pattern if re.match(pattern, date_str): return date_str # Already in ISO format return date_str else: try: # Parse the input date string as a datetime object datetime_obj = datetime.datetime.strptime(date_str, "%Y-%m-%d") # Convert to ISO format iso_date = datetime_obj.isoformat() + "Z" return iso_date except ValueError: return None # Invalid date format return None def compose(self, device : Device) -> Dict: # compose device iterating through the components hardware = self._yang_context.create_data_path('/ietf-network-hardware-inventory:network-hardware-inventory') network_elements = hardware.create_path('network-elements') hardware = self._yang_context.create_data_path('/ietf-hardware:hardware') network_element = network_elements.create_path('network-element[uuid="{:s}"]'.format(device.device_id.device_uuid.uuid)) network_element.create_path('uuid', device.device_id.device_uuid.uuid) network_element.create_path('name', device.name) components = network_element.create_path('components') physical_index = 1 for component in device.components: attributes = component.attributes component_new = hardware.create_path('component[name="{:s}"]'.format(component.name)) component_new = components.create_path('component[uuid="{:s}"]'.format(component.component_uuid.uuid)) component_new.create_path('name', component.name) #Cambiar las clases especiales, su formato y añadir isfru component_type = component.type if component_type == "TRANSCEIVER" : component_type = "module" if component_type == "FRU" : component_type = "slack" component_new.create_path('is-fru', True) else : component_new.create_path('is-fru', False) component_type = component_type.replace("_", "-").lower() component_type = 'iana-hardware:' + component_type component_new.create_path('class', component_type) #Añadir resto de atributos en IETF physical_index += 1 component_new.create_path('physical-index', physical_index) component_new.create_path('description', attributes["description"]) component_new.create_path('parent', component.parent) component_new.create_path('description', attributes["description"].replace('/"',"")) if "CHASSIS" not in component.type: parent_component_references = component_new.create_path('parent-component-references') parent = parent_component_references.create_path('component-reference[index="{:d}"]'.format(physical_index)) for component_parent in device.components: if component.parent == component_parent.name : parent.create_path('uuid', component_parent.component_uuid.uuid) break if attributes["mfg-date"] != "": mfg_date = self.convert_to_iso_date(attributes["mfg-date"]) LOGGER.info('component[name="{:s}"]'.format(attributes["mfg-date"])) component_new.create_path('mfg-date', mfg_date) component_new.create_path('hardware-rev', attributes["hardware-rev"]) Loading @@ -111,20 +101,29 @@ class YangHandler: component_new.create_path('firmware-rev', attributes["firmware-version"]) component_new.create_path('serial-num', attributes["serial-num"]) component_new.create_path('mfg-name', attributes["mfg-name"]) if attributes["removable"]: removable = attributes["removable"].lower() if 'true' in removable: component_new.create_path('is-fru', True) elif 'false' in removable: component_new.create_path('is-fru', False) if attributes["id"]: component_new.create_path('parent-rel-pos', attributes["id"]) try: if "CHASSIS" in component.type : component_new.create_path('parent-rel-pos', 0) else: parent_rel_pos = int(attributes["id"].replace("\"", "")) component_new.create_path('parent-rel-pos', parent_rel_pos) except ValueError: LOGGER.info('ERROR:{:s} '.format(component.name )) continue component_new.create_path('uri', component.name) component_new.create_path('uuid', component.component_uuid.uuid) contains_child = [] for component2 in device.components: if component.name == component2.parent : contains_child.append(component2.name) component_new.create_path('contains-child', contains_child) for child in device.components: if component.name == child.parent : component_new.create_path('contained-child', child.component_uuid.uuid) return json.loads(hardware.print_mem('json')) Loading src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py +5 −2 Original line number Diff line number Diff line Loading @@ -13,9 +13,12 @@ # limitations under the License. from nbi.service.rest_server.nbi_plugins.ietf_hardware.Hardware import Hardware from nbi.service.rest_server.nbi_plugins.ietf_hardware.HardwareMultipleDevices import HardwareMultipleDevices from nbi.service.rest_server.RestServer import RestServer URL_PREFIX = "/restconf/data/device=<path:device_uuid>/ietf-hardware:hardware" URL_PREFIX_DEVICE = "/restconf/data/device=<path:device_uuid>/ietf-network-hardware-inventory:network-hardware-inventory" URL_PREFIX_HARDWARE = "/restconf/data/ietf-network-hardware-inventory:network-hardware-inventory" def register_ietf_hardware(rest_server: RestServer): rest_server.add_resource(Hardware, URL_PREFIX) No newline at end of file rest_server.add_resource(Hardware, URL_PREFIX_DEVICE) rest_server.add_resource(HardwareMultipleDevices, URL_PREFIX_HARDWARE) src/nbi/service/rest_server/nbi_plugins/ietf_hardware/yang/ietf-network-hardware-inventory@2023-03-09.yang 0 → 100644 +604 −0 File added.Preview size limit exceeded, changes collapsed. Show changes src/nbi/service/rest_server/nbi_plugins/ietf_hardware/Hardware.py +1 −1 File changed.Contains only whitespace changes. Show changes Loading
src/nbi/service/rest_server/nbi_plugins/ietf_hardware/HardwareMultipleDevices.py 0 → 100644 +50 −0 Original line number Diff line number Diff line # Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (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. import logging from flask import request from flask.json import jsonify from flask_restful import Resource from common.proto.context_pb2 import Empty from context.client.ContextClient import ContextClient from ..tools.Authentication import HTTP_AUTH from ..tools.HttpStatusCodes import HTTP_OK, HTTP_SERVERERROR from .YangHandler import YangHandler LOGGER = logging.getLogger(__name__) class HardwareMultipleDevices(Resource): @HTTP_AUTH.login_required def get(self): LOGGER.debug('Request: {:s}'.format(str(request))) try: context_client = ContextClient() list_devices = context_client.ListDevices(Empty()) LOGGER.info('Request: {:s}'.format(str(list_devices))) hardware_list_reply = [] yang_handler = YangHandler() for device in list_devices.devices: hardware_reply = yang_handler.compose(device) hardware_list_reply.append(hardware_reply) yang_handler.destroy() response = jsonify(hardware_list_reply) response.status_code = HTTP_OK except Exception as e: # pylint: disable=broad-except MSG = 'Something went wrong Retrieving Hardware of Devices({:s})' response = jsonify({'error': str(e)}) response.status_code = HTTP_SERVERERROR return response
src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py +48 −49 Original line number Diff line number Diff line Loading @@ -12,19 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. import libyang, os from common.proto.context_pb2 import Device from typing import Dict, Optional import datetime import json import logging import libyang import os import re import datetime LOGGER = logging.getLogger(__name__) YANG_DIR = os.path.join(os.path.dirname(__file__), 'yang') YANG_MODULES = [ 'iana-hardware', 'ietf-hardware' 'ietf-hardware', 'ietf-network-hardware-inventory' ] class YangHandler: Loading @@ -35,7 +37,7 @@ class YangHandler: self._yang_context.load_module(yang_module_name).feature_enable_all() def parse_to_dict(self, message : Dict) -> Dict: yang_module = self._yang_context.get_module('ietf-hardware') yang_module = self._yang_context.get_module('ietf-network-hardware-inventory') dnode : Optional[libyang.DNode] = yang_module.parse_data_dict( message, validate_present=True, validate=True, strict=True ) Loading @@ -44,66 +46,54 @@ class YangHandler: dnode.free() return message @staticmethod def convert_to_iso_date(date_str: str) -> Optional[str]: date_str = date_str.strip('"') # Define the regex pattern for ISO 8601 date format pattern = r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-]\d{2}:\d{2})" # Check if the input date string matches the pattern if re.match(pattern, date_str): return date_str # Already in ISO format return date_str else: try: # Parse the input date string as a datetime object datetime_obj = datetime.datetime.strptime(date_str, "%Y-%m-%d") # Convert to ISO format iso_date = datetime_obj.isoformat() + "Z" return iso_date except ValueError: return None # Invalid date format return None def compose(self, device : Device) -> Dict: # compose device iterating through the components hardware = self._yang_context.create_data_path('/ietf-network-hardware-inventory:network-hardware-inventory') network_elements = hardware.create_path('network-elements') hardware = self._yang_context.create_data_path('/ietf-hardware:hardware') network_element = network_elements.create_path('network-element[uuid="{:s}"]'.format(device.device_id.device_uuid.uuid)) network_element.create_path('uuid', device.device_id.device_uuid.uuid) network_element.create_path('name', device.name) components = network_element.create_path('components') physical_index = 1 for component in device.components: attributes = component.attributes component_new = hardware.create_path('component[name="{:s}"]'.format(component.name)) component_new = components.create_path('component[uuid="{:s}"]'.format(component.component_uuid.uuid)) component_new.create_path('name', component.name) #Cambiar las clases especiales, su formato y añadir isfru component_type = component.type if component_type == "TRANSCEIVER" : component_type = "module" if component_type == "FRU" : component_type = "slack" component_new.create_path('is-fru', True) else : component_new.create_path('is-fru', False) component_type = component_type.replace("_", "-").lower() component_type = 'iana-hardware:' + component_type component_new.create_path('class', component_type) #Añadir resto de atributos en IETF physical_index += 1 component_new.create_path('physical-index', physical_index) component_new.create_path('description', attributes["description"]) component_new.create_path('parent', component.parent) component_new.create_path('description', attributes["description"].replace('/"',"")) if "CHASSIS" not in component.type: parent_component_references = component_new.create_path('parent-component-references') parent = parent_component_references.create_path('component-reference[index="{:d}"]'.format(physical_index)) for component_parent in device.components: if component.parent == component_parent.name : parent.create_path('uuid', component_parent.component_uuid.uuid) break if attributes["mfg-date"] != "": mfg_date = self.convert_to_iso_date(attributes["mfg-date"]) LOGGER.info('component[name="{:s}"]'.format(attributes["mfg-date"])) component_new.create_path('mfg-date', mfg_date) component_new.create_path('hardware-rev', attributes["hardware-rev"]) Loading @@ -111,20 +101,29 @@ class YangHandler: component_new.create_path('firmware-rev', attributes["firmware-version"]) component_new.create_path('serial-num', attributes["serial-num"]) component_new.create_path('mfg-name', attributes["mfg-name"]) if attributes["removable"]: removable = attributes["removable"].lower() if 'true' in removable: component_new.create_path('is-fru', True) elif 'false' in removable: component_new.create_path('is-fru', False) if attributes["id"]: component_new.create_path('parent-rel-pos', attributes["id"]) try: if "CHASSIS" in component.type : component_new.create_path('parent-rel-pos', 0) else: parent_rel_pos = int(attributes["id"].replace("\"", "")) component_new.create_path('parent-rel-pos', parent_rel_pos) except ValueError: LOGGER.info('ERROR:{:s} '.format(component.name )) continue component_new.create_path('uri', component.name) component_new.create_path('uuid', component.component_uuid.uuid) contains_child = [] for component2 in device.components: if component.name == component2.parent : contains_child.append(component2.name) component_new.create_path('contains-child', contains_child) for child in device.components: if component.name == child.parent : component_new.create_path('contained-child', child.component_uuid.uuid) return json.loads(hardware.print_mem('json')) Loading
src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py +5 −2 Original line number Diff line number Diff line Loading @@ -13,9 +13,12 @@ # limitations under the License. from nbi.service.rest_server.nbi_plugins.ietf_hardware.Hardware import Hardware from nbi.service.rest_server.nbi_plugins.ietf_hardware.HardwareMultipleDevices import HardwareMultipleDevices from nbi.service.rest_server.RestServer import RestServer URL_PREFIX = "/restconf/data/device=<path:device_uuid>/ietf-hardware:hardware" URL_PREFIX_DEVICE = "/restconf/data/device=<path:device_uuid>/ietf-network-hardware-inventory:network-hardware-inventory" URL_PREFIX_HARDWARE = "/restconf/data/ietf-network-hardware-inventory:network-hardware-inventory" def register_ietf_hardware(rest_server: RestServer): rest_server.add_resource(Hardware, URL_PREFIX) No newline at end of file rest_server.add_resource(Hardware, URL_PREFIX_DEVICE) rest_server.add_resource(HardwareMultipleDevices, URL_PREFIX_HARDWARE)
src/nbi/service/rest_server/nbi_plugins/ietf_hardware/yang/ietf-network-hardware-inventory@2023-03-09.yang 0 → 100644 +604 −0 File added.Preview size limit exceeded, changes collapsed. Show changes
src/nbi/service/rest_server/nbi_plugins/ietf_hardware/Hardware.py +1 −1 File changed.Contains only whitespace changes. Show changes