From 32492d843d878a98c0402a48c773e5cea644aca3 Mon Sep 17 00:00:00 2001 From: armingol Date: Fri, 21 Jun 2024 13:33:07 +0200 Subject: [PATCH 1/5] Inventory NBI in Webuo --- .../ietf_hardware/HardwareMultipleDevices.py | 36 ++++++++++++++++ .../nbi_plugins/ietf_hardware/YangHandler.py | 41 +++---------------- .../nbi_plugins/ietf_hardware/__init__.py | 8 +++- .../nbi_plugins/tfs_api/Resources.py | 9 ---- .../nbi_plugins/tfs_api/__init__.py | 10 +++-- 5 files changed, 54 insertions(+), 50 deletions(-) create mode 100644 src/nbi/service/rest_server/nbi_plugins/ietf_hardware/HardwareMultipleDevices.py diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/HardwareMultipleDevices.py b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/HardwareMultipleDevices.py new file mode 100644 index 000000000..5258455e5 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/HardwareMultipleDevices.py @@ -0,0 +1,36 @@ +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 \ No newline at end of file diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py index 88c9887c0..f0212b01f 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py @@ -98,8 +98,12 @@ class YangHandler: component_new.create_path('serial-num', attributes["serial-num"]) component_new.create_path('mfg-name', attributes["mfg-name"]) if attributes["id"]: - component_new.create_path('parent-rel-pos', attributes["id"]) - + try: + parent_rel_pos = int(attributes["id"].replace("\"", "")) + component_new.create_path('parent-rel-pos', parent_rel_pos) + except ValueError: + continue + component_new.create_path('uri', component.name) @@ -113,39 +117,6 @@ class YangHandler: component_new.create_path('contains-child', contains_child) return hardware.print_mem('json') - - - - '''# example methods (based on openconfig, to be adapted): - #str_path = '/interfaces/interface[name={:s}]'.format(if_name) - if_name = 'my-if' - interfaces = self._yang_context.create_data_path('/openconfig-interfaces:interfaces') - my_if = interfaces.create_path('interface[name="{:s}"]'.format(if_name)) - my_if.create_path('config/name', if_name) - my_if.create_path('config/enabled', True) - - my_subifs = my_if.create_path('subinterfaces') - - subif_index = 3 - my_subif = my_subifs.create_path('subinterface[index="{:d}"]'.format(subif_index)) - my_subif.create_path('config/index', subif_index) - my_subif.create_path('config/enabled', True) - - vlan_id = 123 - my_subif_vlan = my_subif.create_path('openconfig-vlan:vlan') - my_subif_vlan.create_path('match/single-tagged/config/vlan-id', vlan_id) - - my_subif_ipv4 = my_subif.create_path('openconfig-if-ip:ipv4') - my_subif_ipv4.create_path('config/enabled', True) - - my_subif_ipv4_addrs = my_subif_ipv4.create_path('addresses') - my_ipv4_addr_ip = '10.0.1.10' - my_ipv4_addr_prefix = 24 - my_subif_ipv4_addr = my_subif_ipv4_addrs.create_path('address[ip="{:s}"]'.format(my_ipv4_addr_ip)) - my_subif_ipv4_addr.create_path('config/ip', my_ipv4_addr_ip) - my_subif_ipv4_addr.create_path('config/prefix-length', my_ipv4_addr_prefix) - - return my_if.print_mem('json')''' def destroy(self) -> None: diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py index 7f4e219ff..2b1621a2a 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py @@ -1,7 +1,11 @@ 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=/ietf-hardware:hardware" +URL_PREFIX_device = "/restconf/data/device=/ietf-hardware:hardware" +URL_PREFIX_hardware = "/restconf/data/ietf-hardware:hardware" 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) + \ No newline at end of file diff --git a/src/nbi/service/rest_server/nbi_plugins/tfs_api/Resources.py b/src/nbi/service/rest_server/nbi_plugins/tfs_api/Resources.py index b5fd18971..9311c915a 100644 --- a/src/nbi/service/rest_server/nbi_plugins/tfs_api/Resources.py +++ b/src/nbi/service/rest_server/nbi_plugins/tfs_api/Resources.py @@ -176,15 +176,6 @@ class Devices(_Resource): class Device(_Resource): def get(self, device_uuid : str): return format_grpc_to_json(self.client.GetDevice(grpc_device_id(device_uuid))) -class Deviceshw(_Resource): - def get(self, device_uuid : str): - device =format_grpc_to_json(self.client.GetDevice(grpc_device_id(device_uuid))) - yang_handler = YangHandler('ietf-hardware') - - hardware_reply = yang_handler.compose(device) - device = jsonify(hardware_reply) - - return device class LinkIds(_Resource): def get(self): diff --git a/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py b/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py index cbb38e6f2..569c26c4e 100644 --- a/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py +++ b/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py @@ -13,10 +13,11 @@ # limitations under the License. from nbi.service.rest_server.RestServer import RestServer +from nbi.service.rest_server.nbi_plugins.ietf_hardware import Hardware, HardwareMultipleDevices from .Resources import ( Connection, ConnectionIds, Connections, Context, ContextIds, Contexts, - Device, DeviceIds, Devices, Deviceshw, + Device, DeviceIds, Devices, DummyContexts, Link, LinkIds, Links, PolicyRule, PolicyRuleIds, PolicyRules, @@ -48,10 +49,11 @@ RESOURCES = [ ('api.slice', Slice, '/context//slice/'), ('api.device_ids', DeviceIds, '/device_ids'), - ('api.devices', Devices, '/devices'), - ('api.device', Device, '/device/'), + '''('api.devices', Devices, '/devices'),''' + '''('api.device', Device, '/device/'),''' + ('api.devices', HardwareMultipleDevices, '/devices'), + ('api.device', Hardware, '/device/'), - ('api.deviceshw', Deviceshw, '/device//hardware'), ('api.link_ids', LinkIds, '/link_ids'), ('api.links', Links, '/links'), -- GitLab From 0762893a4d685ec77eed20d9860d03f174721ea2 Mon Sep 17 00:00:00 2001 From: armingol Date: Wed, 3 Jul 2024 08:51:26 +0200 Subject: [PATCH 2/5] change to IETF network hardware inventory --- .../nbi_plugins/ietf_hardware/YangHandler.py | 35 +- .../nbi_plugins/ietf_hardware/__init__.py | 4 +- ...network-hardware-inventory@2023-03-09.yang | 604 ++++++++++++++++++ 3 files changed, 629 insertions(+), 14 deletions(-) create mode 100644 src/nbi/service/rest_server/nbi_plugins/ietf_hardware/yang/ietf-network-hardware-inventory@2023-03-09.yang diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py index 3888b7708..4ddf8522a 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py @@ -24,7 +24,8 @@ 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: @@ -35,7 +36,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 ) @@ -65,15 +66,19 @@ class YangHandler: def compose(self, device : Device) -> Dict: - # compose device iterating through the components - - hardware = self._yang_context.create_data_path('/ietf-hardware:hardware') + hardware = self._yang_context.create_data_path('/ietf-network-hardware-inventory:network-hardware-inventory') + network_elements = hardware.create_path('network-elements') + + 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 @@ -95,15 +100,18 @@ class YangHandler: #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) + parent_component_references = component_new.create_path('parent-component-references') + parent = parent_component_references.create_path('component-reference[index="{:d}"]'.format(physical_index)) + for component2 in device.components: + if component.parent == component2.name : + parent.create_path('uuid', component2.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"]) @@ -123,12 +131,15 @@ class YangHandler: component_new.create_path('uuid', component.component_uuid.uuid) - contains_child = [] + contained_child = [] for component2 in device.components: if component.name == component2.parent : - contains_child.append(component2.name) + child_uuid = component2.component_uuid.uuid.strip("'") + contained_child.append(child_uuid) + LOGGER.info('parent: {:s}'.format(str(component))) + LOGGER.info('child: {:s}'.format(str(component2))) - component_new.create_path('contains-child', contains_child) + component_new.create_path('contained-child', contained_child) return json.loads(hardware.print_mem('json')) diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py index d3f7c6375..acec9ac45 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py @@ -16,8 +16,8 @@ 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_device = "/restconf/data/device=/ietf-hardware:hardware" -URL_PREFIX_hardware = "/restconf/data/ietf-hardware:hardware" +URL_PREFIX_device = "/restconf/data/device=/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_device) diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/yang/ietf-network-hardware-inventory@2023-03-09.yang b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/yang/ietf-network-hardware-inventory@2023-03-09.yang new file mode 100644 index 000000000..e074e3005 --- /dev/null +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/yang/ietf-network-hardware-inventory@2023-03-09.yang @@ -0,0 +1,604 @@ +module ietf-network-hardware-inventory { + yang-version 1.1; + namespace + "urn:ietf:params:xml:ns:yang:ietf-network-hardware-inventory"; + prefix nhi; + + import ietf-yang-types { + prefix yang; + reference + "RFC6991: Common YANG Data Types."; + } + + import iana-hardware { + prefix ianahw; + reference + "https://www.iana.org/assignments/yang-parameters"; + } + + import ietf-inet-types { + prefix inet; + reference + "RFC6991: Common YANG Data Types."; + } + + organization + "IETF CCAMP Working Group"; + contact + "WG Web: + WG List: + + Editor: Chaode Yu + + + Editor: Italo Busi + + + Editor: Aihua Guo + + + Editor: Sergio Belotti + + + Editor: Jean-Francois Bouquier + + + Editor: Fabio Peruzzini + "; + + description + "This module defines a model for retrieving network hardware + inventory. + + The model fully conforms to the Network Management + Datastore Architecture (NMDA). + Copyright (c) 2022 IETF Trust and the persons + identified as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Revised BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC XXXX; see + the RFC itself for full legal notices. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL + NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'NOT RECOMMENDED', + 'MAY', and 'OPTIONAL' in this document are to be interpreted as + described in BCP 14 (RFC 2119) (RFC 8174) when, and only when, + they appear in all capitals, as shown here."; + + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + + revision 2023-03-09 { + description + "Initial version"; + reference + "RFC XXXX: A YANG Data Model for Network Hardware Inventory."; + //RFC Editor: replace XXXX with actual RFC number, update date + //information and remove this note + } + + container network-hardware-inventory { + config false; + description + "The top-level container for the network inventory + information."; + uses equipment-rooms-grouping; + uses network-elements-grouping; + } + + grouping common-entity-attributes { + description + "A set of attributes which are common to all the entities + (e.g., component, equipment room) defined in this module."; + leaf uuid { + type yang:uuid; + description + "Uniquely identifies an entity (e.g., component)."; + } + leaf name { + type string; + description + "A name for an entity (e.g., component), as specified by + a network manager, that provides a non-volatile 'handle' + for the entity and that can be modified anytime during the + entity lifetime. + + If no configured value exists, the server MAY set the value + of this node to a locally unique value in the operational + state."; + } + leaf description { + type string; + description "a textual description of inventory object"; + } + leaf alias { + type string; + description + "a alias name of inventory objects. This alias name can be + specified by network manager."; + } + } + + grouping network-elements-grouping { + description + "The attributes of the network elements."; + container network-elements { + description + "The container for the list of network elements."; + list network-element { + key uuid; + description + "The list of network elements within the network."; + uses common-entity-attributes; + container ne-location { + description + "The location information of this network element."; + leaf-list equipment-room-name { + type leafref { + path "/nhi:network-hardware-inventory/" + + "nhi:equipment-rooms/nhi:equipment-room/nhi:name"; + } + description + "Names of equipment rooms where the NE is located. + Please note that a NE could be located in several + equipment rooms."; + } + } + uses ne-specific-info-grouping; + uses components-grouping; + } + } + } + + grouping ne-specific-info-grouping { + description + "Attributes applicable to network elements."; + leaf hardware-rev { + type string; + description + "The vendor-specific hardware revision string for the NE."; + } + leaf software-rev { + type string; + description + "The vendor-specific software revision string for the NE."; + } + leaf mfg-name { + type string; + description "The name of the manufacturer of this NE"; + } + leaf mfg-date { + type yang:date-and-time; + description "The date of manufacturing of the NE."; + } + leaf part-number { + type string; + description + "The vendor-specific model name identifier string associated + with this NE. The preferred value is the customer-visible + part number, which may be printed on the NE itself."; + } + leaf serial-number { + type string; + description + "The vendor-specific serial number string for the NE"; + } + leaf product-name { + type string; + description + "indicates the vendor-spefic device type infomation."; + } + } + + grouping equipment-rooms-grouping { + description + "The attributes of the equipment rooms."; + container equipment-rooms { + description + "The container for the list of equipment rooms."; + list equipment-room { + key uuid; + description + "The list of equipment rooms within the network."; + uses common-entity-attributes; + leaf location { + type string; + description + "compared with the location information of the other + inventory objects, a GIS address is preferred for + equipment room"; + } + container racks { + description + "Top level container for the list of racks."; + list rack { + key uuid; + description + "The list of racks within an equipment room."; + uses common-entity-attributes; + uses rack-specific-info-grouping; + list contained-chassis { + key "ne-ref component-ref"; + description + "The list of chassis within a rack."; + leaf ne-ref { + type leafref { + path "/nhi:network-hardware-inventory" + + "/nhi:network-elements/nhi:network-element" + + "/nhi:uuid"; + } + description + "The reference to the network element containing + the chassis component."; + } + leaf component-ref { + type leafref { + path "/nhi:network-hardware-inventory" + + "/nhi:network-elements/nhi:network-element" + + "[nhi:uuid=current()/../ne-ref]/nhi:components" + + "/nhi:component/nhi:uuid"; + } + description + "The reference to the chassis component within + the network element and contained by the rack."; + } + leaf relative-position { + type uint8; + description "A relative position of chassis within + the rack"; + } + } + } + } + } + } + } + + grouping rack-specific-info-grouping { + description + "Attributes applicable to racks only."; + container rack-location { + description + "The location information of the rack, which comprises the + name of the equipment room, row number, and column number."; + leaf equipment-room-name { + type leafref { + path "/nhi:network-hardware-inventory/nhi:equipment-rooms" + + "/nhi:equipment-room/nhi:name"; + } + description + "Name of equipment room where this rack is located."; + } + leaf row-number { + type uint32; + description + "Identifies the row within the equipment room where + the rack is located."; + } + leaf column-number { + type uint32; + description + "Identifies the physical location of the rack within + the column."; + } + } + leaf height { + type uint16; + units millimeter; + description + "Rack height."; + } + leaf width { + type uint16; + units millimeter; + description + "Rack width."; + } + leaf depth { + type uint16; + units millimeter; + description + "Rack depth."; + } + leaf max-voltage { + type uint16; + units volt; + description + "The maximum voltage could be supported by the rack."; + } + } + + grouping components-grouping { + description + "The attributes of the hardware components."; + container components { + description + "The container for the list of components."; + list component { + key uuid; + description + "The list of components within a network element."; + uses common-entity-attributes; + leaf location { + type string; + description + "A relative location information of this component. + In optical transport network, the location string is + using the following pattern: + '/ne=[/r=][/sh= + [/s_sh= ...]][[/sl= + [/s_sl= ...]][/p= …]]' + "; + } + leaf class { + type identityref { + base ianahw:hardware-class; + } + description + "An indication of the general hardware type of the + component."; + reference + "RFC 8348: A YANG Data Model for Hardware Management."; + } + leaf-list contained-child { + type leafref { + path "../nhi:uuid"; + } + description + "The list of the identifiers of the child components + physically contained within this component."; + } + leaf parent-rel-pos { + type int32 { + range "0 .. 2147483647"; + } + description + "The relative position with respect to the parent + component among all the sibling components."; + reference + "RFC 6933: Entity MIB (Version 4) - + entPhysicalParentRelPos"; + } + + container parent-component-references { + description + "The top level container for the list of the + identifiers of the parents of this component in a + hierarchy."; + list component-reference { + key index; + description + "The list of the identifiers of the parents of this + component in a hierarchy. + + The index parameter defines the hierarchy: the topmost + parent has an index of 0."; + leaf index { + type uint8; + description + "The index of the parent with respect to the + hierarchy."; + } + leaf class { + type leafref { + path "../../../nhi:class"; + } + description + "Class of the hierarchial parent component."; + } + leaf uuid { + type leafref { + path "../../../nhi:uuid"; + } + description + "The identifier of the parent's component in the + hierarchy."; + } + } + } + + leaf hardware-rev { + type string; + description + "The vendor-specific hardware revision string for the + component. The preferred value is the hardware revision + identifier actually printed on the component itself (if + present)."; + reference + "RFC 6933: Entity MIB (Version 4) - + entPhysicalHardwareRev"; + } + leaf firmware-rev { + type string; + description + "The vendor-specific firmware revision string for the + component."; + reference + "RFC 6933: Entity MIB (Version 4) - + entPhysicalFirmwareRev"; + } + leaf software-rev { + type string; + description + "The vendor-specific software revision string for the + component."; + reference + "RFC 6933: Entity MIB (Version 4) - + entPhysicalSoftwareRev"; + } + leaf serial-num { + type string; + description + "The vendor-specific serial number string for the + component. The preferred value is the serial number + string actually printed on the component itself (if + present)."; + reference + "RFC 6933: Entity MIB (Version 4) - + entPhysicalSerialNum"; + } + leaf mfg-name { + type string; + description + "The name of the manufacturer of this physical component. + The preferred value is the manufacturer name string + actually printed on the component itself (if present). + + Note that comparisons between instances of the + 'model-name', 'firmware-rev', 'software-rev', and + 'serial-num' nodes are only meaningful amongst + components with the same value of 'mfg-name'. + + If the manufacturer name string associated with the + physical component is unknown to the server, then this + node is not instantiated."; + reference + "RFC 6933: Entity MIB (Version 4) - entPhysicalMfgName"; + } + leaf part-number { + type string; + description + "The vendor-specific model name identifier string + associated with this physical component. The preferred + value is the customer-visible part number, which may be + printed on the component itself. + + If the model name string associated with the physical + component is unknown to the server, then this node is + not instantiated."; + reference + "RFC 6933: Entity MIB (Version 4) - + entPhysicalModelName"; + } + leaf asset-id { + type string; + description + "This node is a user-assigned asset tracking identifier + for the component. + + A server implementation MAY map this leaf to the + entPhysicalAssetID MIB object. Such an implementation + needs to use some mechanism to handle the differences in + size and characters allowed between this leaf and + entPhysicalAssetID. The definition of such a mechanism + is outside the scope of this document."; + reference + "RFC 6933: Entity MIB (Version 4) - entPhysicalAssetID"; + } + leaf is-fru { + type boolean; + description + "This node indicates whether or not this component is + considered a 'field-replaceable unit' by the vendor. If + this node contains the value 'true', then this component + identifies a field-replaceable unit. For all components + that are permanently contained within a + field-replaceable unit, the value 'false' should be + returned for this node."; + reference + "RFC 6933: Entity MIB (Version 4) - entPhysicalIsFRU"; + } + leaf mfg-date { + type yang:date-and-time; + description + "The date of manufacturing of the managed component."; + reference + "RFC 6933: Entity MIB (Version 4) - entPhysicalMfgDate"; + } + leaf-list uri { + type inet:uri; + description + "This node contains identification information about the + component."; + reference + "RFC 6933: Entity MIB (Version 4) - entPhysicalUris"; + } + uses component-specific-info-grouping; + } + } + } + + grouping component-specific-info-grouping { + description + "In case if there are some missing attributes of component not + defined by RFC8348. These attributes could be + component-specific. + Here we provide a extension structure for all the components + we recognized. We will enrich these component specifc + containers in the future."; + choice component-class { + description + "This extension differs between different component + classes."; + case chassis { + when "./class = 'ianahw:chassis'"; + container chassis-specific-info { + description + "This container contains some attributes belong to + chassis only."; + uses chassis-specific-info-grouping; + } + } + case container { + when "./class = 'ianahw:container'"; + container slot-specific-info { + description + "This container contains some attributes belong to + slot or sub-slot only."; + uses slot-specific-info-grouping; + } + } + case module { + when "./nhi:class = 'ianahw:module'"; + container board-specific-info { + description + "This container contains some attributes belong to + board only."; + uses board-specific-info-grouping; + } + } + case port { + when "./nhi:class = 'ianahw:port'"; + container port-specific-info { + description + "This container contains some attributes belong to + port only."; + uses port-specific-info-grouping; + } + } + //TO BE ADDED: transceiver + } + } + + grouping chassis-specific-info-grouping { + //To be enriched in the future. + description + "Specific attributes applicable to chassis only."; + } + + grouping slot-specific-info-grouping { + //To be enriched in the future. + description + "Specific attributes applicable to slots only."; + } + + grouping board-specific-info-grouping { + //To be enriched in the future. + description + "Specific attributes applicable to boards only."; + } + + grouping port-specific-info-grouping { + //To be enriched in the future. + description + "Specific attributes applicable to ports only."; + } +} -- GitLab From f69da29d9369a3f75b34655d0c86c5a24ae0aeb7 Mon Sep 17 00:00:00 2001 From: armingol Date: Fri, 26 Jul 2024 08:34:26 +0200 Subject: [PATCH 3/5] add attributes to Inventory NBI --- .../nbi_plugins/ietf_hardware/YangHandler.py | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py index 4ddf8522a..aa0a90908 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py @@ -12,13 +12,14 @@ # 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') @@ -88,9 +89,6 @@ class YangHandler: 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 @@ -101,14 +99,15 @@ class YangHandler: physical_index += 1 - component_new.create_path('description', attributes["description"]) - - parent_component_references = component_new.create_path('parent-component-references') - parent = parent_component_references.create_path('component-reference[index="{:d}"]'.format(physical_index)) - for component2 in device.components: - if component.parent == component2.name : - parent.create_path('uuid', component2.component_uuid.uuid) - break + 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"]) @@ -119,11 +118,22 @@ 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"]: try: - parent_rel_pos = int(attributes["id"].replace("\"", "")) - component_new.create_path('parent-rel-pos', parent_rel_pos) + 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) @@ -131,15 +141,9 @@ class YangHandler: component_new.create_path('uuid', component.component_uuid.uuid) - contained_child = [] - for component2 in device.components: - if component.name == component2.parent : - child_uuid = component2.component_uuid.uuid.strip("'") - contained_child.append(child_uuid) - LOGGER.info('parent: {:s}'.format(str(component))) - LOGGER.info('child: {:s}'.format(str(component2))) - - component_new.create_path('contained-child', contained_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')) -- GitLab From e47151d6cd9ffc0a23a128bd2ee0d75dc5e257ea Mon Sep 17 00:00:00 2001 From: gifrerenom Date: Mon, 9 Sep 2024 14:31:09 +0000 Subject: [PATCH 4/5] Pre-merge code cleanup --- .../nbi_plugins/ietf_hardware/Hardware.py | 2 +- .../ietf_hardware/HardwareMultipleDevices.py | 16 +++++++++++++++- .../nbi_plugins/ietf_hardware/YangHandler.py | 13 +++++-------- .../nbi_plugins/ietf_hardware/__init__.py | 9 ++++----- .../rest_server/nbi_plugins/tfs_api/__init__.py | 5 ++--- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/Hardware.py b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/Hardware.py index a7404b924..2282de557 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/Hardware.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/Hardware.py @@ -50,4 +50,4 @@ class Hardware(Resource): LOGGER.exception(MSG.format(str(device_uuid))) response = jsonify({'error': str(e)}) response.status_code = HTTP_SERVERERROR - return response \ No newline at end of file + return response diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/HardwareMultipleDevices.py b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/HardwareMultipleDevices.py index 5258455e5..b1beff518 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/HardwareMultipleDevices.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/HardwareMultipleDevices.py @@ -1,3 +1,17 @@ +# 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 @@ -33,4 +47,4 @@ class HardwareMultipleDevices(Resource): MSG = 'Something went wrong Retrieving Hardware of Devices({:s})' response = jsonify({'error': str(e)}) response.status_code = HTTP_SERVERERROR - return response \ No newline at end of file + return response diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py index aa0a90908..33611adcf 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py @@ -89,10 +89,9 @@ class YangHandler: if component_type == "FRU" : component_type = "slack" - + 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 @@ -100,7 +99,7 @@ class YangHandler: physical_index += 1 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)) @@ -124,7 +123,7 @@ class YangHandler: component_new.create_path('is-fru', True) elif 'false' in removable: component_new.create_path('is-fru', False) - + if attributes["id"]: try: if "CHASSIS" in component.type : @@ -137,8 +136,6 @@ class YangHandler: continue component_new.create_path('uri', component.name) - - component_new.create_path('uuid', component.component_uuid.uuid) for child in device.components: @@ -146,6 +143,6 @@ class YangHandler: component_new.create_path('contained-child', child.component_uuid.uuid) return json.loads(hardware.print_mem('json')) - + def destroy(self) -> None: - self._yang_context.destroy() \ No newline at end of file + self._yang_context.destroy() diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py index acec9ac45..ba774650e 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/__init__.py @@ -16,10 +16,9 @@ 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_device = "/restconf/data/device=/ietf-network-hardware-inventory:network-hardware-inventory" -URL_PREFIX_hardware = "/restconf/data/ietf-network-hardware-inventory:network-hardware-inventory" +URL_PREFIX_DEVICE = "/restconf/data/device=/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_device) - rest_server.add_resource(HardwareMultipleDevices, URL_PREFIX_hardware) - \ No newline at end of file + rest_server.add_resource(Hardware, URL_PREFIX_DEVICE) + rest_server.add_resource(HardwareMultipleDevices, URL_PREFIX_HARDWARE) diff --git a/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py b/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py index e67c297bc..1f667f134 100644 --- a/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py +++ b/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py @@ -49,9 +49,8 @@ RESOURCES = [ ('api.slice', Slice, '/context//slice/'), ('api.device_ids', DeviceIds, '/device_ids'), - - ('api.devices', HardwareMultipleDevices, '/devices'), - ('api.device', Hardware, '/device/'), + ('api.devices', Devices, '/devices'), + ('api.device', Device, '/device/'), ('api.link_ids', LinkIds, '/link_ids'), ('api.links', Links, '/links'), -- GitLab From 90bb0d507e65250f9d9fe83579e6ee2e18d9bb82 Mon Sep 17 00:00:00 2001 From: armingol Date: Mon, 9 Sep 2024 16:42:02 +0200 Subject: [PATCH 5/5] pre merge code cleanup --- .../nbi_plugins/ietf_hardware/YangHandler.py | 25 +++---------------- .../nbi_plugins/tfs_api/__init__.py | 3 +-- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py index 33611adcf..7662261e9 100644 --- a/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py +++ b/src/nbi/service/rest_server/nbi_plugins/ietf_hardware/YangHandler.py @@ -46,60 +46,45 @@ 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: hardware = self._yang_context.create_data_path('/ietf-network-hardware-inventory:network-hardware-inventory') network_elements = hardware.create_path('network-elements') - + 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 = 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_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('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)) @@ -107,7 +92,6 @@ class YangHandler: 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"]) component_new.create_path('mfg-date', mfg_date) @@ -137,7 +121,6 @@ class YangHandler: component_new.create_path('uri', component.name) component_new.create_path('uuid', component.component_uuid.uuid) - for child in device.components: if component.name == child.parent : component_new.create_path('contained-child', child.component_uuid.uuid) diff --git a/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py b/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py index 1f667f134..41e8ff1ea 100644 --- a/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py +++ b/src/nbi/service/rest_server/nbi_plugins/tfs_api/__init__.py @@ -13,7 +13,6 @@ # limitations under the License. from nbi.service.rest_server.RestServer import RestServer -from nbi.service.rest_server.nbi_plugins.ietf_hardware import Hardware, HardwareMultipleDevices from .Resources import ( Connection, ConnectionIds, Connections, Context, ContextIds, Contexts, @@ -51,7 +50,7 @@ RESOURCES = [ ('api.device_ids', DeviceIds, '/device_ids'), ('api.devices', Devices, '/devices'), ('api.device', Device, '/device/'), - + ('api.link_ids', LinkIds, '/link_ids'), ('api.links', Links, '/links'), ('api.link', Link, '/link/'), -- GitLab