Revision e3b66e4b
b/docs/source/index.rst | ||
---|---|---|
1 | 1 |
Welcome |
2 | 2 |
======= |
3 | 3 |
|
4 |
``ncclient`` is a Python library for NETCONF clients. It aims to offer an intuitive API that sensibly maps the XML-encoded nature of NETCONF to Python constructs and idioms, and make writing network-management scripts easier. Other key features are: |
|
4 |
``ncclient`` is a Python library for NETCONF clients. It aims to offer an intuitive API that |
|
5 |
sensibly maps the XML-encoded nature of NETCONF to Python constructs and idioms, and make writing |
|
6 |
network-management scripts easier. Other key features are: |
|
5 | 7 |
|
6 | 8 |
* Supports all operations and capabilities defined in :rfc:`4741`. |
7 | 9 |
* Request pipelining. |
... | ... | |
9 | 11 |
* Keeping XML out of the way unless really needed. |
10 | 12 |
* Extensible. New transport mappings and capabilities/operations can be easily added. |
11 | 13 |
|
12 |
It is suitable for Python 2.6+ (not Python 3 yet, though), and depends on `paramiko <http://www.lag.net/paramiko/>`_, an SSH library. |
|
14 |
It is suitable for Python 2.6+ (not Python 3 yet, though), and depends on `paramiko |
|
15 |
<http://www.lag.net/paramiko/>`_, an SSH library. |
|
13 | 16 |
|
14 | 17 |
The best way to introduce is of course, through a simple code example:: |
15 | 18 |
|
b/docs/source/manager.rst | ||
---|---|---|
10 | 10 |
These attributes control what capabilties are exchanged with the NETCONF server and what operations |
11 | 11 |
are available through the `Manager` API. |
12 | 12 |
|
13 |
.. autodata:: CAPABILITIES |
|
14 |
|
|
15 | 13 |
.. autodata:: OPERATIONS |
16 | 14 |
|
15 |
.. autodata:: CAPABILITIES |
|
16 |
|
|
17 | 17 |
Factory functions |
18 | 18 |
----------------- |
19 | 19 |
|
b/docs/source/operations.rst | ||
---|---|---|
20 | 20 |
Operations |
21 | 21 |
---------- |
22 | 22 |
|
23 |
*TODO* The operation classes are currently undocumented. See documentation of |
|
24 |
`~ncclient.manager.Manager` for methods that utilize the operation classes. The parameters accepted |
|
25 |
by :meth:`~RPC.request` for these classes are the same. |
|
23 |
*TODO* |
|
24 |
|
|
25 |
The operation classes are currently undocumented. See documentation of `~ncclient.manager.Manager` |
|
26 |
for methods that utilize the operation classes. The parameters accepted by :meth:`~RPC.request` for |
|
27 |
these classes are the same. |
|
26 | 28 |
|
27 | 29 |
Replies with data |
28 | 30 |
----------------- |
b/docs/source/xml_.rst | ||
---|---|---|
1 |
:mod:`~ncclient.xml_` -- XML Handling
|
|
1 |
:mod:`~ncclient.xml_` -- XML handling
|
|
2 | 2 |
===================================== |
3 | 3 |
|
4 |
*TODO* |
|
4 |
.. module:: ncclient.xml_ |
|
5 |
:synopsis: XML handling |
|
6 |
|
|
7 |
.. autoexception:: XMLError |
|
8 |
:show-inheritance: |
|
9 |
|
|
10 |
Namespaces |
|
11 |
----------- |
|
12 |
|
|
13 |
.. autodata:: BASE_NS_1_0 |
|
14 |
|
|
15 |
.. autodata:: TAILF_AAA_1_1 |
|
16 |
|
|
17 |
.. autodata:: TAILF_EXECD_1_1 |
|
18 |
|
|
19 |
.. autodata:: CISCO_CPI_1_0 |
|
20 |
|
|
21 |
.. autodata:: FLOWMON_1_0 |
|
22 |
|
|
23 |
.. function:: register_namespace(prefix, uri) |
|
24 |
|
|
25 |
ElementTree's namespace map determines the prefixes for namespace URI's when serializing to XML. |
|
26 |
This method allows modifying this map to specify a prefix for a namespace URI. |
|
27 |
|
|
28 |
.. autofunction:: qualify |
|
29 |
|
|
30 |
Conversion |
|
31 |
----------- |
|
32 |
|
|
33 |
.. autofunction:: to_xml |
|
34 |
|
|
35 |
.. autofunction:: to_ele |
|
36 |
|
|
37 |
.. autofunction:: parse_root |
|
38 |
|
|
39 |
.. autofunction:: validated_element |
|
40 |
|
|
41 |
.. |
b/ncclient/manager.py | ||
---|---|---|
21 | 21 |
import logging |
22 | 22 |
logger = logging.getLogger('ncclient.manager') |
23 | 23 |
|
24 |
|
|
25 |
CAPABILITIES = capabilities.Capabilities([ |
|
24 |
CAPABILITIES = [ |
|
26 | 25 |
"urn:ietf:params:netconf:base:1.0", |
27 | 26 |
"urn:ietf:params:netconf:capability:writable-running:1.0", |
28 | 27 |
"urn:ietf:params:netconf:capability:candidate:1.0", |
... | ... | |
35 | 34 |
"urn:liberouter:params:netconf:capability:power-control:1.0" |
36 | 35 |
"urn:ietf:params:netconf:capability:interleave:1.0" |
37 | 36 |
#'urn:ietf:params:netconf:capability:notification:1.0', # TODO |
38 |
]) |
|
39 |
"""`~ncclient.capabilities.Capabilities` object representing the client's capabilities. This is used |
|
40 |
during the initial capability exchange. Modify this if you need to announce some capability not |
|
41 |
already included. |
|
37 |
] |
|
38 |
"""A list of URI's representing the client's capabilities. This is used during the initial |
|
39 |
capability exchange. Modify this if you need to announce some capability not already included. |
|
42 | 40 |
""" |
43 | 41 |
|
44 | 42 |
OPERATIONS = { |
... | ... | |
57 | 55 |
"poweroff_machine": operations.PoweroffMachine, |
58 | 56 |
"reboot_machine": operations.RebootMachine |
59 | 57 |
} |
60 |
"""Dictionary of method names and corresponding `~ncclient.operations.RPC` subclasses. `Manager` |
|
61 |
uses this to lookup operations, e.g. "get_config" is mapped to `~ncclient.operations.GetConfig`. It |
|
62 |
is thus possible to add additional operations to the `Manager` API. |
|
63 |
""" |
|
58 |
"""Dictionary of method names and corresponding `~ncclient.operations.RPC` subclasses. It is used to |
|
59 |
lookup operations, e.g. "get_config" is mapped to `~ncclient.operations.GetConfig`. It is thus |
|
60 |
possible to add additional operations to the `Manager` API.""" |
|
64 | 61 |
|
65 | 62 |
def connect_ssh(*args, **kwds): |
66 | 63 |
"""Initializes a NETCONF session over SSH, and creates a connected `Manager` instance. *host* |
... | ... | |
114 | 111 |
|
115 | 112 |
:rtype: `Manager` |
116 | 113 |
""" |
117 |
session = transport.SSHSession(CAPABILITIES)
|
|
114 |
session = transport.SSHSession(capabilities.Capabilities(CAPABILITIES))
|
|
118 | 115 |
session.load_known_hosts() |
119 | 116 |
session.connect(*args, **kwds) |
120 | 117 |
return Manager(session) |
b/ncclient/xml_.py | ||
---|---|---|
12 | 12 |
# See the License for the specific language governing permissions and |
13 | 13 |
# limitations under the License. |
14 | 14 |
|
15 |
|
|
16 | 15 |
"Methods for creating, parsing, and dealing with XML and ElementTree objects." |
17 | 16 |
|
18 | 17 |
from cStringIO import StringIO |
... | ... | |
25 | 24 |
### Namespace-related |
26 | 25 |
|
27 | 26 |
#: Base NETCONF namespace |
28 |
BASE_NS_1_0 = 'urn:ietf:params:xml:ns:netconf:base:1.0'
|
|
27 |
BASE_NS_1_0 = "urn:ietf:params:xml:ns:netconf:base:1.0"
|
|
29 | 28 |
#: Namespace for Tail-f core data model |
30 |
TAILF_AAA_1_1 = 'http://tail-f.com/ns/aaa/1.1'
|
|
29 |
TAILF_AAA_1_1 = "http://tail-f.com/ns/aaa/1.1"
|
|
31 | 30 |
#: Namespace for Tail-f execd data model |
32 |
TAILF_EXECD_1_1 = 'http://tail-f.com/ns/execd/1.1'
|
|
31 |
TAILF_EXECD_1_1 = "http://tail-f.com/ns/execd/1.1"
|
|
33 | 32 |
#: Namespace for Cisco data model |
34 |
CISCO_CPI_1_0 = 'http://www.cisco.com/cpi_10/schema'
|
|
33 |
CISCO_CPI_1_0 = "http://www.cisco.com/cpi_10/schema"
|
|
35 | 34 |
#: Namespace for Flowmon data model |
36 |
FLOWMON_1_0 = 'http://www.liberouter.org/ns/netopeer/flowmon/1.0'
|
|
35 |
FLOWMON_1_0 = "http://www.liberouter.org/ns/netopeer/flowmon/1.0"
|
|
37 | 36 |
|
38 | 37 |
try: |
39 | 38 |
register_namespace = ET.register_namespace |
... | ... | |
43 | 42 |
# cElementTree uses ElementTree's _namespace_map, so that's ok |
44 | 43 |
ElementTree._namespace_map[uri] = prefix |
45 | 44 |
|
46 |
prefix_map = {
|
|
45 |
for (ns, pre) in {
|
|
47 | 46 |
BASE_NS_1_0: 'nc', |
48 | 47 |
TAILF_AAA_1_1: 'aaa', |
49 | 48 |
TAILF_EXECD_1_1: 'execd', |
50 | 49 |
CISCO_CPI_1_0: 'cpi', |
51 | 50 |
FLOWMON_1_0: 'fm', |
52 |
} |
|
51 |
}.items(): register_namespace(pre, ns)
|
|
53 | 52 |
|
54 |
for (ns, pre) in prefix_map.items():
|
|
55 |
register_namespace(pre, ns)
|
|
53 |
qualify = lambda tag, ns=BASE_NS_1_0: tag if ns is None else "{%s}%s" % (ns, tag)
|
|
54 |
"""Qualify a tag name with a namespace, in :mod:`~xml.etree.ElementTree` fashion i.e. *{namespace}tagname*.
|
|
56 | 55 |
|
57 |
qualify = lambda tag, ns=BASE_NS_1_0: tag if ns is None else '{%s}%s' % (ns, tag) |
|
56 |
:arg tag: name of the tag |
|
57 |
:type arg: `string` |
|
58 | 58 |
|
59 |
#unqualify = lambda tag: tag[tag.rfind('}')+1:] |
|
59 |
:arg ns: namespace to qualify with |
|
60 |
:type ns: `string` |
|
61 |
""" |
|
60 | 62 |
|
61 | 63 |
def to_xml(ele, encoding="UTF-8"): |
62 |
"""Convert an :class:`~xml.etree.ElementTree.Element` to XML.
|
|
64 |
"""Convert an `~xml.etree.ElementTree.Element` to XML. |
|
63 | 65 |
|
64 |
:arg ele: the :class:`~xml.etree.ElementTree.Element`
|
|
66 |
:arg ele: the `~xml.etree.ElementTree.Element` |
|
65 | 67 |
:arg encoding: character encoding |
66 |
:rtype: :obj:`string`
|
|
68 |
:rtype: `string` |
|
67 | 69 |
""" |
68 | 70 |
xml = ET.tostring(ele, encoding) |
69 | 71 |
return xml if xml.startswith('<?xml') else '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml) |
70 | 72 |
|
71 | 73 |
def to_ele(x): |
72 |
"""Convert XML to :class:`~xml.etree.ElementTree.Element`. |
|
74 |
"""Convert XML to `~xml.etree.ElementTree.Element`. If passed an |
|
75 |
`~xml.etree.ElementTree.Element` simply returns that. |
|
73 | 76 |
|
74 |
:type xml: :obj:`string` |
|
75 |
:rtype: :class:`~xml.etree.ElementTree.Element` |
|
77 |
:arg x: the XML document or element |
|
78 |
:type x: `string` or `~xml.etree.ElementTree.Element` |
|
79 |
:rtype: `~xml.etree.ElementTree.Element` |
|
76 | 80 |
""" |
77 |
return x if iselement(x) else ET.fromstring(x) |
|
78 |
|
|
79 |
iselement = ET.iselement |
|
81 |
return x if ET.iselement(x) else ET.fromstring(x) |
|
80 | 82 |
|
81 | 83 |
def parse_root(raw): |
82 | 84 |
"""Efficiently parses the root element of an XML document. |
83 | 85 |
|
84 | 86 |
:arg raw: XML document |
85 |
:returns: a tuple of `(tag, attrib)`, where *tag* is the (qualified) |
|
86 |
name of the element and *attrib* is a dictionary of its attributes. |
|
87 |
:returns: a tuple of ``(tag, attrib)``, where *tag* is the (qualified) name of the element and *attrib* is a dictionary of its attributes. |
|
87 | 88 |
:rtype: `tuple` |
88 | 89 |
""" |
89 | 90 |
fp = StringIO(raw) |
... | ... | |
91 | 92 |
return (element.tag, element.attrib) |
92 | 93 |
|
93 | 94 |
def validated_element(x, tags=None, attrs=None): |
94 |
"Checks if the root element of an XML document or Element meets the supplied criteria." |
|
95 |
"""Checks if the root element of an XML document or Element meets the supplied criteria. |
|
96 |
|
|
97 |
:arg tags: allowable tag name or sequence of allowable alternatives |
|
98 |
:type tags: `string` or sequence of strings |
|
99 |
|
|
100 |
:arg attrs: list of required attributes, each of which may be a sequence of several allowable alternatives |
|
101 |
:type attrs: sequence of strings or sequence of sequences of strings |
|
102 |
|
|
103 |
:raises: `XMLError` if the requirements are not met |
|
104 |
""" |
|
95 | 105 |
ele = to_ele(x) |
96 | 106 |
if tags: |
97 | 107 |
if isinstance(tags, basestring): |
... | ... | |
108 | 118 |
raise XMLError("Element [%s] does not have required attributes" % ele.tag) |
109 | 119 |
return ele |
110 | 120 |
|
111 |
|
|
112 | 121 |
new_ele = lambda tag, attrs={}, **extra: ET.Element(tag, attrs, **extra) |
113 | 122 |
|
114 | 123 |
sub_ele = lambda parent, tag, attrs={}, **extra: ET.SubElement(parent, tag, attrs, **extra) |
Also available in: Unified diff