get_schema_node

        High-level api: Given an Element node in config, get_schema_node returns
        a schema node (defined in RFC 6020), which is an Element node in the
        schema tree.

        Parameters
        ----------

        config_node : `Element`
            An Element node in config tree.

        Returns
        -------

        Element
            A schema node, or None when nothing can be found.

        Raises
        ------

        ModelError
            If identifier is not unique in a namespace.


        Code Example::

            >>> device.nc.load_model('openconfig-interfaces')
            >>> reply = device.nc.get_config(models='openconfig-interfaces')
            >>> config = device.nc.extract_config(reply)
            >>> print(config)
            ...
            >>> config.ns
            ...
            >>> config_nodes = config.xpath('/nc:config/oc-if:interfaces/oc-if:interface[oc-if:name="GigabitEthernet0/0"]')
            >>> config_node = config_nodes[0]
            >>>
            >>> device.nc.get_schema_node(config_node)
            <Element {http://openconfig.net/yang/interfaces}interface at 0xf11acfcc>
            >>>

def get_schema_node(self, config_node):
        '''get_schema_node

        High-level api: Given an Element node in config, get_schema_node returns
        a schema node (defined in RFC 6020), which is an Element node in the
        schema tree.

        Parameters
        ----------

        config_node : `Element`
            An Element node in config tree.

        Returns
        -------

        Element
            A schema node, or None when nothing can be found.

        Raises
        ------

        ModelError
            If identifier is not unique in a namespace.


        Code Example::

            >>> device.nc.load_model('openconfig-interfaces')
            >>> reply = device.nc.get_config(models='openconfig-interfaces')
            >>> config = device.nc.extract_config(reply)
            >>> print(config)
            ...
            >>> config.ns
            ...
            >>> config_nodes = config.xpath('/nc:config/oc-if:interfaces/oc-if:interface[oc-if:name="GigabitEthernet0/0"]')
            >>> config_node = config_nodes[0]
            >>>
            >>> device.nc.get_schema_node(config_node)
            <Element {http://openconfig.net/yang/interfaces}interface at 0xf11acfcc>
            >>>
        '''

        def get_child(parent, tag):
            children = [i for i in parent.iter(tag=tag) \
                        if i.attrib['type'] != 'choice' and \
                           i.attrib['type'] != 'case' and \
                           is_parent(parent, i)]
            if len(children) == 1:
                return children[0]
            elif len(children) > 1:
                if parent.getparent() is None:
                    raise ModelError("more than one root has tag '{}'" \
                                     .format(tag))
                else:
                    raise ModelError("node {} has more than one child with " \
                                     "tag '{}'" \
                                     .format(self.get_xpath(parent), tag))
            else:
                return None

        def is_parent(node1, node2):
            ancestors = {id(a): a for a in node2.iterancestors()}
            ids_1 = set([id(a) for a in node1.iterancestors()])
            ids_2 = set([id(a) for a in node2.iterancestors()])
            if not ids_1 < ids_2:
                return False
            for i in ids_2 - ids_1:
                if ancestors[i] is not node1 and \
                   ancestors[i].attrib['type'] != 'choice' and \
                   ancestors[i].attrib['type'] != 'case':
                    return False
            return True

        n = Composer(self, config_node)
        config_path = n.path
        if ' '.join(config_path) in self.nodes:
            return self.nodes[' '.join(config_path)]
        if len(config_path) > 1:
            parent = self.get_schema_node(config_node.getparent())
            if parent is None:
                return None
            else:
                child = get_child(parent, config_node.tag)
                if child is not None:
                    self.nodes[' '.join(config_path)] = child
                return child
        else:
            tree = self.models[n.model_name].tree
            child = get_child(tree, config_node.tag)
            if child is not None:
                self.nodes[' '.join(config_path)] = child
            return child