cybot16 / NED-Miration-Utility

NED Migration utility is a script that facilitates the ned migration process for devices in NSO
Other
5 stars 2 forks source link

AttributeError: 'ActionParams' object has no attribute 'affected_services_with_changes' #2

Open ngrundler opened 2 years ago

ngrundler commented 2 years ago

Using this tool to attempt a migration on a Juniper device and it's failing with the following traceback:

Traceback (most recent call last):
  File "/home/grundler/src/NED-Miration-Utility/./ned_migration.py", line 633, in <module>
    ned_migrate(root, device, new_ned_id, dry_run , no_networking)
  File "/home/grundler/src/NED-Miration-Utility/./ned_migration.py", line 472, in ned_migrate
    if device_migrate_dr_result.affected_services.as_list() or device_migrate_dr_result.affected_services_with_changes.as_list():
  File "/opt/ncs/current/src/ncs/pyapi/ncs/maagic.py", line 478, in __getattr__
    super(Node, self).__getattribute__(name)
AttributeError: 'ActionParams' object has no attribute 'affected_services_with_changes'

This is from an NSO 5.6.3.1 system-install.

ngrundler commented 2 years ago

Checking tailf-ncs-devices.yang, the affected-services-with-changes leaf was removed in 5.6:

...
  revision 2021-09-02 {                                                                                                                                                                                                                                                                                                       
    description                                                                                                                                                                                                                                                                                                               
      "Released as part of NCS-5.6.

      Non-backwards-compatible changes have been introduced. 

      ...

      Updated the description of the 'affected-services' leaf in the                                                                                                                                                                                                                                                         
      migrate action output parameters.                                                                                                                                                                                                                                                                                      

      Removed the 'affected-services-with-changes' leaf in the                                                                                                                                                                                                                                                               
      migrate action output parameters.    

      ...
}  
...
rd101010 commented 1 year ago

Also see this on a local install of 5.7.6.2 against a Cisco iosxr netsim.

Markhamm commented 1 year ago

3

Hi, the above PR should fix the issue. As you have noticed, in NSO 5.6 and later, the 'affected-services' leaf has been removed, the PR is emulating the old behavior however performances are quite bad as we need to scan backpointers for each device config in order to identifiy if some of the modified paths proposed by the new NED impact the device config. We have warned the BU about this behavior and an ongoing request has been made to change the behavior of the "migrate" command.

I forgot ... To get the behavior being fixed with the PR, you need to patch maagic.py file (/opt/ncs/current/src/ncs/pyapi/ncs/maagic.py) and add the following method to the Node class:

def _get_metadata(self):
    """
    leveraging file .$NCS_DIR/erlang/econfd/include/econfd.hrl
    for unofficially supported attributes
    (read - not in C documentation)
    %% Attribute values
    %% CONFD_ATTR_TAGS: value is C_LIST of C_BUF/C_STR
    -define(CONFD_ATTR_TAGS,       16#80000000).
    %% CONFD_ATTR_ANNOTATION: value is C_BUF/C_STR
    -define(CONFD_ATTR_ANNOTATION, 16#80000001).
    %% CONFD_ATTR_INACTIVE: value is 'true' (C_BOOL)
    -define(CONFD_ATTR_INACTIVE,   16#00000000).
    %% NCS specific attributes
    %% value = int32, used by NCS fastmap code
    -define(CONFD_ATTR_REFCOUNT,  16#80000002).
    %% value is [instance-identifier], used by fastmap code
    -define(CONFD_ATTR_BACKPOINTER,  16#80000003).
    -define(CONFD_ATTR_WHEN,  16#80000004).
    -define(CONFD_ATTR_ORIGINAL_VALUE,  16#80000005).

    NOTE: MUST EXCLUDE OPER DATA or
    Unavailable functionality (44): Attributes are not available for operational data
    """
    supported_attrs = [_tm.ATTR_TAGS, _tm.ATTR_ANNOTATION,
                       _tm.ATTR_INACTIVE, _tm.ATTR_BACKPOINTER]
    # ATTR_REFCOUNT, ATTR_WHEN and ATTR_ORIGINAL_VALUE are
    # not imported from erlang to C library
    # but seems still available
    ATTR_REFCOUNT = 0x80000002
    ATTR_WHEN = 0x80000004
    ATTR_ORIGINAL_VALUE = 0x80000005
    unsupported_attrs = [ATTR_REFCOUNT, ATTR_WHEN, ATTR_ORIGINAL_VALUE]

    all_attrs = supported_attrs + unsupported_attrs
    # TODO - what if already populated
    if self._path == '':
        # no metadata on root
        return
    # Exclude oper data
    if self._cs_node.is_oper():
        return

    # Arbitrary default values - TBC
    # For now a dict but probably some object would be better
    attributes = {
        'tags': [],
        'annotation': "",
        'inactive': False,
        'refcount': -1,
        'backpointer': [],
        'when': None,
        'original_value': None
        }
    try:
        if self._backend:
            attrs = self._backend.get_attrs(attrs=all_attrs,
                                            keypath=self._path)
            for attr in attrs:
                if attr.attr == _tm.ATTR_TAGS:
                    attributes['tags'] = [str(tag) for tag in attr.v]
                elif attr.attr == _tm.ATTR_ANNOTATION:
                    attributes['annotation'] = [str(attr.v)]
                elif attr.attr == _tm.ATTR_INACTIVE:
                    attributes['inactive'] = attr.v
                elif attr.attr == _tm.ATTR_BACKPOINTER:
                    # Here making it nice..
                    attributes['backpointer'] = [str(bp) for bp in attr.v]
                elif attr.attr == ATTR_REFCOUNT:
                    attributes['refcount'] = int(attr.v)
                elif attr.attr == ATTR_WHEN:
                    attributes['when'] = str(attr.v)
                elif attr.attr == ATTR_ORIGINAL_VALUE:
                    attributes['original_value'] = str(attr.v)
                else:
                    print("Unsupported attribute")
                    print(attr.attr)
                    print(attr.v)
        self._set_attr('_metadata', attributes)
    except:
        pass

and then modify the getattr() method of the same class like this:

    def __getattr__(self, name):
        """Python magic method."""
        if not self._populated:
            self._populate()
            self._get_metadata()      # <===== Add this line
            return getattr(self, name)

No need to restart NSO

Markhamm commented 1 year ago

4

As been added. It fixes several things and improve the perf + avoid the need the of Maagic patch.