def node_sub(self, node_self, node_other):
        '''node_sub

        High-level api: Compute the delta of two config nodes. This method is
        recursive, assuming two config nodes are different.

        Parameters
        ----------

        node_self : `Element`
            A config node in the destination config that is being processed.
            node_self cannot be a leaf node.

        node_other : `Element`
            A config node in the source config that is being processed.

        Returns
        -------

        tuple
            There are three elements in the tuple: a list of gNMI Path
            instances that need to be deleted, a list of gNMI Update instances
            for replacement purpose, and a list of gNMI Update instances for
            merging purpose.
        '''

        paths_delete = []
        updates_replace = []
        updates_update = []
        done_list = []

        # if a leaf-list node, delete the leaf-list totally
        # if a list node, by default delete the list instance
        # if a list node and delete_whole=True, delete the list totally
        def generate_delete(node, instance=True):
            paths_delete.append(gNMIComposer(self.device, node) \
                        .get_path(instance=instance))

        # if a leaf-list node, replace the leaf-list totally
        # if a list node, replace the list totally
        def generate_replace(node, instance=True):
            n = gNMIComposer(self.device, node)
            json_value = n.get_json(instance=instance).encode()
            value = TypedValue(json_val=json_value)
            path = n.get_path(instance=instance)
            updates_replace.append(Update(path=path, val=value))

        # if a leaf-list node, update the leaf-list totally
        # if a list node, by default update the list instance
        # if a list node and update_whole=True, update the list totally
        def generate_update(node, instance=True):
            n = gNMIComposer(self.device, node)
            json_value = n.get_json(instance=instance).encode()
            value = TypedValue(json_val=json_value)
            path = n.get_path(instance=instance)
            updates_update.append(Update(path=path, val=value))

        # the leaf-list value sequence under node_self is different from the one
        # under node_other
        def leaf_list_seq_is_different(tag):
            if [i.text for i in node_self.iterchildren(tag=tag)] == \
               [i.text for i in node_other.iterchildren(tag=tag)]:
                return False
            else:
                return True

        # the leaf-list value set under node_self is different from the one
        # under node_other
        def leaf_list_set_is_different(tag):
            s_list = [i.text for i in node_self.iterchildren(tag=tag)]
            o_list = [i.text for i in node_other.iterchildren(tag=tag)]
            if set(s_list) == set(o_list):
                return False
            else:
                return True

        # the leaf-list or list under node_self is empty
        def list_is_empty(tag):
            if [i for i in node_self.iterchildren(tag=tag)]:
                return False
            else:
                return True

        # the sequence of list instances under node_self is different from the
        # one under node_other
        def list_seq_is_different(tag):
            s_list = [i for i in node_self.iterchildren(tag=tag)]
            o_list = [i for i in node_other.iterchildren(tag=tag)]
            if [self.device.get_xpath(n) for n in s_list] == \
               [self.device.get_xpath(n) for n in o_list]:
                return False
            else:
                return True

        # all list instances under node_self have peers under node_other, and
        # the sequence of list instances under node_self that have peers under
        # node_other is same as the sequence of list instances under node_other
        def list_seq_is_inclusive(tag):
            s_list = [i for i in node_self.iterchildren(tag=tag)]
            o_list = [i for i in node_other.iterchildren(tag=tag)]
            s_seq = [self.device.get_xpath(n) for n in s_list]
            o_seq = [self.device.get_xpath(n) for n in o_list]
            if set(s_seq) <= set(o_seq) and \
               [i for i in s_seq if i in o_seq] == o_seq:
                return True
            else:
                return False

        in_s_not_in_o, in_o_not_in_s, in_s_and_in_o = \
            self._group_kids(node_self, node_other)
        for child_s in in_s_not_in_o:
            schema_node = self.device.get_schema_node(child_s)
            if schema_node.get('type') == 'leaf':
                generate_update(child_s)
            elif schema_node.get('type') == 'leaf-list':
                if child_s.tag not in done_list:
                    generate_replace(child_s, instance=False)
                    done_list.append(child_s.tag)
            elif schema_node.get('type') == 'container':
                generate_update(child_s)
            elif schema_node.get('type') == 'list':
                if schema_node.get('ordered-by') == 'user':
                    if child_s.tag not in done_list:
                        generate_replace(child_s, instance=False)
                        done_list.append(child_s.tag)
                else:
                    generate_update(child_s, instance=True)
        for child_o in in_o_not_in_s:
            schema_node = self.device.get_schema_node(child_o)
            if schema_node.get('type') == 'leaf':
                generate_delete(child_o)
            elif schema_node.get('type') == 'leaf-list':
                if child_o.tag not in done_list:
                    child_s = node_self.find(child_o.tag)
                    if child_s is None:
                        generate_delete(child_o, instance=False)
                    else:
                        generate_replace(child_s, instance=False)
                    done_list.append(child_o.tag)
            elif schema_node.get('type') == 'container':
                generate_delete(child_o)
            elif schema_node.get('type') == 'list':
                if schema_node.get('ordered-by') == 'user':
                    if list_seq_is_inclusive(child_o.tag):
                        generate_delete(child_o, instance=True)
                    else:
                        if child_o.tag not in done_list:
                            generate_replace(child_o, instance=False)
                            done_list.append(child_o.tag)
                else:
                    if list_is_empty(child_o.tag):
                        if child_o.tag not in done_list:
                            generate_delete(child_o, instance=False)
                            done_list.append(child_o.tag)
                    else:
                        generate_delete(child_o, instance=True)
        for child_s, child_o in in_s_and_in_o:
            schema_node = self.device.get_schema_node(child_s)
            if schema_node.get('type') == 'leaf':
                if child_s.text != child_o.text:
                    generate_update(child_s)
            elif schema_node.get('type') == 'leaf-list':
                if child_s.tag not in done_list:
                    if schema_node.get('ordered-by') == 'user':
                        if leaf_list_seq_is_different(child_s.tag):
                            generate_replace(child_s, instance=False)
                    else:
                        if leaf_list_set_is_different(child_s.tag):
                            generate_replace(child_s, instance=False)
                    done_list.append(child_s.tag)
            elif schema_node.get('type') == 'container':
                if BaseCalculator(self.device, child_s, child_o).ne:
                    d, r, u = self.node_sub(child_s, child_o)
                    paths_delete += d
                    updates_replace += r
                    updates_update += u
            elif schema_node.get('type') == 'list':
                if schema_node.get('ordered-by') == 'user':
                    if list_seq_is_different(child_s.tag):
                        if child_s.tag not in done_list:
                            generate_replace(child_s, instance=False)
                            done_list.append(child_s.tag)
                        else:
                            if BaseCalculator(self.device, child_s, child_o).ne:
                                d, r, u = self.node_sub(child_s, child_o)
                                paths_delete += d
                                updates_replace += r
                                updates_update += u
                else:
                    if BaseCalculator(self.device, child_s, child_o).ne:
                        d, r, u = self.node_sub(child_s, child_o)
                        paths_delete += d
                        updates_replace += r
                        updates_update += u
        return (paths_delete, updates_replace, updates_update)
def node_sub ( self , node_self , node_other ) : paths_delete = [ ] updates_replace = [ ] updates_update = [ ] done_list = [ ] def generate_delete ( node , instance = True ) : paths_delete . append ( gNMIComposer ( self . device , node ) . get_path ( instance = instance ) ) def generate_replace ( node , instance = True ) : n = gNMIComposer ( self . device , node ) json_value = n . get_json ( instance = instance ) . encode ( ) value = TypedValue ( json_val = json_value ) path = n . get_path ( instance = instance ) updates_replace . append ( Update ( path = path , val = value ) ) def generate_update ( node , instance = True ) : n = gNMIComposer ( self . device , node ) json_value = n . get_json ( instance = instance ) . encode ( ) value = TypedValue ( json_val = json_value ) path = n . get_path ( instance = instance ) updates_update . append ( Update ( path = path , val = value ) ) def leaf_list_seq_is_different ( tag ) : if [ i . text for i in node_self . iterchildren ( tag = tag ) ] == [ i . text for i in node_other . iterchildren ( tag = tag ) ] : return False else : return True def leaf_list_set_is_different ( tag ) : s_list = [ i . text for i in node_self . iterchildren ( tag = tag ) ] o_list = [ i . text for i in node_other . iterchildren ( tag = tag ) ] if set ( s_list ) == set ( o_list ) : return False else : return True def list_is_empty ( tag ) : if [ i for i in node_self . iterchildren ( tag = tag ) ] : return False else : return True def list_seq_is_different ( tag ) : s_list = [ i for i in node_self . iterchildren ( tag = tag ) ] o_list = [ i for i in node_other . iterchildren ( tag = tag ) ] if [ self . device . get_xpath ( n ) for n in s_list ] == [ self . device . get_xpath ( n ) for n in o_list ] : return False else : return True def list_seq_is_inclusive ( tag ) : s_list = [ i for i in node_self . iterchildren ( tag = tag ) ] o_list = [ i for i in node_other . iterchildren ( tag = tag ) ] s_seq = [ self . device . get_xpath ( n ) for n in s_list ] o_seq = [ self . device . get_xpath ( n ) for n in o_list ] if set ( s_seq ) <= set ( o_seq ) and [ i for i in s_seq if i in o_seq ] == o_seq : return True else : return False in_s_not_in_o , in_o_not_in_s , in_s_and_in_o = self . _group_kids ( node_self , node_other ) for child_s in in_s_not_in_o : schema_node = self . device . get_schema_node ( child_s ) if schema_node . get ( 'type' ) == 'leaf' : generate_update ( child_s ) elif schema_node . get ( 'type' ) == 'leaf-list' : if child_s . tag not in done_list : generate_replace ( child_s , instance = False ) done_list . append ( child_s . tag ) elif schema_node . get ( 'type' ) == 'container' : generate_update ( child_s ) elif schema_node . get ( 'type' ) == 'list' : if schema_node . get ( 'ordered-by' ) == 'user' : if child_s . tag not in done_list : generate_replace ( child_s , instance = False ) done_list . append ( child_s . tag ) else : generate_update ( child_s , instance = True ) for child_o in in_o_not_in_s : schema_node = self . device . get_schema_node ( child_o ) if schema_node . get ( 'type' ) == 'leaf' : generate_delete ( child_o ) elif schema_node . get ( 'type' ) == 'leaf-list' : if child_o . tag not in done_list : child_s = node_self . find ( child_o . tag ) if child_s is None : generate_delete ( child_o , instance = False ) else : generate_replace ( child_s , instance = False ) done_list . append ( child_o . tag ) elif schema_node . get ( 'type' ) == 'container' : generate_delete ( child_o ) elif schema_node . get ( 'type' ) == 'list' : if schema_node . get ( 'ordered-by' ) == 'user' : if list_seq_is_inclusive ( child_o . tag ) : generate_delete ( child_o , instance = True ) else : if child_o . tag not in done_list : generate_replace ( child_o , instance = False ) done_list . append ( child_o . tag ) else : if list_is_empty ( child_o . tag ) : if child_o . tag not in done_list : generate_delete ( child_o , instance = False ) done_list . append ( child_o . tag ) else : generate_delete ( child_o , instance = True ) for child_s , child_o in in_s_and_in_o : schema_node = self . device . get_schema_node ( child_s ) if schema_node . get ( 'type' ) == 'leaf' : if child_s . text != child_o . text : generate_update ( child_s ) elif schema_node . get ( 'type' ) == 'leaf-list' : if child_s . tag not in done_list : if schema_node . get ( 'ordered-by' ) == 'user' : if leaf_list_seq_is_different ( child_s . tag ) : generate_replace ( child_s , instance = False ) else : if leaf_list_set_is_different ( child_s . tag ) : generate_replace ( child_s , instance = False ) done_list . append ( child_s . tag ) elif schema_node . get ( 'type' ) == 'container' : if BaseCalculator ( self . device , child_s , child_o ) . ne : d , r , u = self . node_sub ( child_s , child_o ) paths_delete += d updates_replace += r updates_update += u elif schema_node . get ( 'type' ) == 'list' : if schema_node . get ( 'ordered-by' ) == 'user' : if list_seq_is_different ( child_s . tag ) : if child_s . tag not in done_list : generate_replace ( child_s , instance = False ) done_list . append ( child_s . tag ) else : if BaseCalculator ( self . device , child_s , child_o ) . ne : d , r , u = self . node_sub ( child_s , child_o ) paths_delete += d updates_replace += r updates_update += u else : if BaseCalculator ( self . device , child_s , child_o ) . ne : d , r , u = self . node_sub ( child_s , child_o ) paths_delete += d updates_replace += r updates_update += u return ( paths_delete , updates_replace , updates_update )
node_sub

        High-level api: Compute the delta of two config nodes. This method is
        recursive, assuming two config nodes are different.

        Parameters
        ----------

        node_self : `Element`
            A config node in the destination config that is being processed.
            node_self cannot be a leaf node.

        node_other : `Element`
            A config node in the source config that is being processed.

        Returns
        -------

        tuple
            There are three elements in the tuple: a list of gNMI Path
            instances that need to be deleted, a list of gNMI Update instances
            for replacement purpose, and a list of gNMI Update instances for
            merging purpose.