node_sub

        High-level api: Compute the delta of two config nodes. This method is
        recursive. Assume 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 Restconf DELETE
            Requests, a list of Restconf PUT Requests, and a list of Restconf
            PATCH Requests.

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

        High-level api: Compute the delta of two config nodes. This method is
        recursive. Assume 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 Restconf DELETE
            Requests, a list of Restconf PUT Requests, and a list of Restconf
            PATCH Requests.
        '''

        deletes = []
        puts = []
        patches = []

        # 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):
            composer = RestconfComposer(self.device, node)
            url = 'https://{}:{}'.format(self.ip, self.port)
            url += composer.get_url(instance=instance)
            deletes.append(requests.Request('DELETE', url, headers=header_json))

        # if a leaf-list node, replace the leaf-list totally
        # if a list node, replace the list totally
        def generate_put(node, instance=True):
            composer = RestconfComposer(self.device, node)
            url = 'https://{}:{}'.format(self.ip, self.port)
            url += composer.get_url(instance=instance)
            data_json = composer.get_json(instance=instance)
            puts.append(requests.Request('PUT', url, headers=header_json,
                                         data=data_json))

        # 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_patch(node, instance=True):
            composer = RestconfComposer(self.device, node)
            url = 'https://{}:{}'.format(self.ip, self.port)
            url += composer.get_url(instance=instance)
            data_json = composer.get_json(instance=instance)
            patches.append(requests.Request('PATCH', url, headers=header_json,
                                            data=data_json))

        # 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' or \
               schema_node.get('type') == 'container':
                generate_patch(child_s)
            elif schema_node.get('type') == 'leaf-list' or \
                 schema_node.get('type') == 'list':
                if schema_node.get('ordered-by') == 'user':
                    return ([], [generate_put(node_self, instance=True)], [])
                else:
                    generate_put(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' or \
               schema_node.get('type') == 'container':
                generate_delete(child_o)
            elif schema_node.get('type') == 'leaf-list' or \
                 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:
                        return ([], [generate_put(node_self, instance=True)],
                                [])
                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_patch(child_s)
            elif schema_node.get('type') == 'leaf-list':
                if schema_node.get('ordered-by') == 'user':
                    if list_seq_is_different(child_s.tag):
                        return ([], [generate_put(node_self, instance=True)],
                                [])
            elif schema_node.get('type') == 'container':
                if BaseCalculator(self.device, child_s, child_o).ne:
                    x, y, z = self.node_sub(child_s, child_o)
                    deletes += x
                    puts += y
                    patches += z
            elif schema_node.get('type') == 'list':
                if schema_node.get('ordered-by') == 'user':
                    if list_seq_is_different(child_s.tag):
                        return ([], [generate_put(node_self, instance=True)],
                                [])
                    else:
                        if BaseCalculator(self.device, child_s, child_o).ne:
                            x, y, z = self.node_sub(child_s, child_o)
                            deletes += x
                            puts += y
                            patches += z
                else:
                    if BaseCalculator(self.device, child_s, child_o).ne:
                        x, y, z = self.node_sub(child_s, child_o)
                        deletes += x
                        puts += y
                        patches += z
        return (deletes, puts, patches)