class TransportError(NCClientError):
pass
-class OperationError(NCClientError):
+class RPCError(NCClientError):
pass
class OperationError(NCClientError):
from xml.etree import cElementTree as ET
+iselement = ET.iselement
### Namespace-related ###
xml = ET.tostring(self._root, encoding)
# some etree versions don't include xml decl with utf-8
# this is a problem with some devices
- if encoding == 'utf-8':
- return ((u'<?xml version="1.0" encoding="utf-8"?>'
- % encoding).encode(encoding) + xml)
- else:
- return xml
+ return (xml if xml.startswith('<?xml')
+ else '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml))
@property
def tree(self):
return spec
elif isinstance(spec, basestring):
return ET.XML(spec)
- # assume isinstance(spec, dict)
- elif 'tag' in spec:
+ ## assume isinstance(spec, dict)
+ if 'tag' in spec:
ele = ET.Element(spec.get('tag'), spec.get('attributes', {}))
- ele.text = str(spec.get('text', ''))
+ ele.text = spec.get('text', '')
children = spec.get('children', [])
- if isinstance(children, dict): children = [children]
+ if isinstance(children, dict):
+ children = [children]
for child in children:
- ET.SubElement(ele, TreeBuilder.build(child))
+ ele.append(XMLConverter.build(child))
return ele
elif 'comment' in spec:
return ET.Comment(spec.get('comment'))
import operations
import transport
-SESSION_TYPES = {
- 'ssh': transport.SSHSession
-}
-
OPERATIONS = {
'get': operations.Get,
'get-config': operations.GetConfig,
'kill-session': operations.KillSession,
}
-class Manager(type):
+def connect_ssh(*args, **kwds):
+ session = transport.SSHSession(capabilities.CAPABILITIES)
+ session.load_system_host_keys()
+ session.connect(*args, **kwds)
+ return Manager(session)
+
+connect = connect_ssh # default
+
+class Manager:
'Facade for the API'
- def connect(self, session_type, *args, **kwds):
- self._session = SESSION_TYPES[session_type](capabilities.CAPABILITIES)
- self._session.connect(*args, **kwds)
-
- def __getattr__(self, name):
- name = name.replace('_', '-')
- if name in OPERATIONS:
- return OPERATIONS[name](self._session).request
- else:
- raise AttributeError
-
- def get(self, *args, **kwds):
- g = operations.Get(self._session)
- reply = g.request(*args, **kwds)
- if reply.errors:
- raise RPCError(reply.errors)
- else:
- return reply.data
+ def __init__(self, session):
+ self._session = session
- def get_config(self, *args, **kwds):
- gc = operations.GetConfig(self._session)
- reply = gc.request(*args, **kwds)
- if reply.errors:
- raise RPCError(reply.errors)
+ def _get(self, type, *args, **kwds):
+ op = OPERATIONS[type](self._session)
+ reply = op.request(*args, **kwds)
+ if not reply.ok:
+ raise reply.errors[0]
else:
return reply.data
+ def request(op, *args, **kwds):
+ op = OPERATIONS[op](self._session)
+ reply = op.request(*args, **kwds)
+ if not reply.ok:
+ raise reply.errors[0]
+ return reply
+
def locked(self, target='running'):
return LockContext(self._session, target)
+
+ get = lambda self, *args, **kwds: self._get('get')
+
+ get_config = lambda self, *args, **kwds: self._get('get-config')
+
+ edit_config = lambda self, *args, **kwds: self.request('edit-config', *args, **kwds)
+
+ copy_config = lambda self, *args, **kwds: self.request('copy-config', *args, **kwds)
+
+ validate = lambda self, *args, **kwds: self.request('validate', *args, **kwds)
+
+ commit = lambda self, *args, **kwds: self.request('commit', *args, **kwds)
+
+ discard_changes = lambda self, *args, **kwds: self.request('discard-changes', *args, **kwds)
+
+ delete_config = lambda self, *args, **kwds: self.request('delete-config', *args, **kwds)
+
+ lock = lambda self, *args, **kwds: self.request('lock', *args, **kwds)
+
+ unlock = lambda self, *args, **kwds: self.request('unlock', *args, **kwds)
+
+ close_session = lambda self, *args, **kwds: self.request('close-session', *args, **kwds)
+
+ kill_session = lambda self, *args, **kwds: self.request('kill-session', *args, **kwds)
+
+ def close(self):
+ try:
+ self.close_session()
+ except:
+ self._session.expect_close()
+ self._session.close()
'NETCONF protocol operations'
-import logging
-logger = logging.getLogger('ncclient.operations')
+from ncclient import NCClientError
+
+class OperationError(NCClientError):
+ pass
+
+class MissingCapabilityError(OperationError):
+ pass
from retrieve import Get, GetConfig
from edit import EditConfig, CopyConfig, DeleteConfig, Validate, Commit, DiscardChanges
from lock import Lock, Unlock
from subscribe import CreateSubscription
+
__all__ = [
+ 'OperationError',
+ 'MissingCapabilityError',
'Get',
'GetConfig',
'EditConfig',
'CloseSession',
'KillSession',
'CreateSubscription',
- # hmm
]
# See the License for the specific language governing permissions and
# limitations under the License.
-from ncclient.capabilities import URI
from ncclient.rpc import RPC
+from ncclient.content import iselement
import util
SPEC = {
'tag': 'edit-config',
- 'children': [
- { 'target': None }
- ]
+ 'children': [ ]
}
- def request(self):
- pass
+ def request(self, target=None, target_url=None, config=None,
+ default_operation=None, test_option=None, error_option=None):
+ util.one_of(target, target_url)
+ spec = EditConfig.SPEC.copy()
+ params = spec['children']
+ params.append({'tag': 'target', 'children': util.store_or_url(target, target_url)})
+ params.append({'tag': 'config', 'children': config})
+ if default_operation is not None:
+ params.append({'tag': 'default-operation', 'text': default_operation})
+ if test_option is not None:
+ params.append({'tag': 'test-option', 'text': test_option})
+ if error_option is not None:
+ params.append({'tag': 'test-option', 'text': test_option})
-
-class DeleteConfig(RPC): # x
+class DeleteConfig(RPC):
SPEC = {
'tag': 'delete-config',
return self._request(spec)
-class CopyConfig(RPC): # x
+class CopyConfig(RPC):
SPEC = {
'tag': 'copy-config',
return self._request(spec)
-class Validate(RPC): # xxxxx
+class Validate(RPC):
+
+ 'config attr shd not include <config> root'
DEPENDS = [':validate']
}
def request(self, source=None, config=None):
- #self.either_or(source, config)
- #
- #if source is None and config is None:
- # raise OperationError('Insufficient parameters')
- #if source is not None and config is not None:
- # raise OperationError('Too many parameters')
- #spec = Validate.SPEC.copy()
- #
util.one_of(source, capability)
+ spec = SPEC.copy()
if source is not None:
spec['children'].append({
- 'tag': 'source',
- 'children': {'tag': source}
+ 'tag': 'source', 'children': {'tag': source}
})
- #
- #else:
- # if isinstance(config, dict):
- # if config['tag'] != 'config':
- # child['tag'] = 'config'
- # child['children'] = config
- # else:
- # child = config
- # elif isinstance(config, Element):
- # pass
- # else:
- # from xml.etree import cElementTree as ET
- # ele = ET.XML(unicode(config))
- # if __(ele.tag) != 'config':
- # pass
- # else:
- # pass
- # spec['children'].append(child)
- #
+ else:
+ spec['children'].append({'tag': 'config', 'children': config})
return self._request(spec)
-class Commit(RPC): # x
+
+class Commit(RPC):
DEPENDS = [':candidate']
return self._request(Commit.SPEC)
-class DiscardChanges(RPC): # x
+class DiscardChanges(RPC):
DEPENDS = [':candidate']
filter['attributes']['select'] = criteria
return filter
-class Get(RPC): # xx
+class GetReply(RPCReply):
+
+ def parse(self):
+ RPCReply.parse(self)
+
+class Get(RPC):
SPEC = {
'tag': 'get',
'children': [ { 'tag': 'source', 'children': {'tag': None } } ]
}
- REPLY_CLS = GetConfigReply
+ REPLY_CLS = GetReply
def request(self, source=None, source_url=None, filter=None):
self._one_of(source, source_url)
# self._assert(':xpath')
spec['children'].append(build_filter(*filter))
return self._request(spec)
-
-class GetReply(RPCReply):
-
- def parse(self):
- RPCReply.parse(self)
# TODO when can actually test it...
-from rpc import RPC
+from ncclient.rpc import RPC
from ncclient.glue import Listener
from ncclient.content import qualify as _
#!/usr/bin/env python
-'boilerplate'
+'Boilerplate'
from ncclient import OperationError
-class MissingCapabilityError(OperationError):
- pass
+from . import MissingCapabilityError
def one_of(self, *args):
for i, arg in enumerate(args):
def assert_capability(key, capabilities):
if key not in capabilities:
- raise MissingCapabilityError
+ raise MissingCapabilityError('[%s] capability is required for this operation' % key)
def store_or_url(store, url):
# limitations under the License.
from rpc import RPC
-from reply import RPCReply
+from reply import RPCReply, RPCError
-from ncclient import RPCError
+import ncclient
-class ReplyTimeoutError(RPCError): pass
+class ReplyTimeoutError(ncclient.RPCError):
+ pass
__all__ = [
'RPC',
'RPCReply',
+ 'RPCError',
'ReplyTimeoutError'
]
from threading import Lock
from weakref import WeakValueDictionary
+from ncclient.glue import Listener
+from ncclient.content import unqualify as __
+
import logging
logger = logging.getLogger('ncclient.rpc.listener')
# See the License for the specific language governing permissions and
# limitations under the License.
+import ncclient
+
from xml.etree import cElementTree as ET
from ncclient.content import multiqualify as _
return self._raw
def parse(self):
- if self._parsed: return
+ if self._parsed:
+ return
+
root = self._root = ET.fromstring(self._raw) # <rpc-reply> element
if __(root.tag) != 'rpc-reply':
if self._errors:
break
- if self.ok:
- # TODO: store children in some way...
- pass
-
self._parsed = True
@property
return self._errors
-class RPCError(Exception): # raise it if you like
+class RPCError(ncclient.RPCError): # raise it if you like
def __init__(self, err_dict):
self._dict = err_dict
if self.message is not None:
- Exception.__init__(self, self.message)
+ ncclient.RPCError.__init__(self, self.message)
else:
- Exception.__init__(self)
+ ncclient.RPCError.__init__(self)
@property
def raw(self):
from uuid import uuid1
from weakref import WeakValueDictionary
-from ncclient.content import TreeBuilder
+from ncclient.content import XMLConverter
from ncclient.content import qualify as _
from ncclient.content import unqualify as __
from ncclient.glue import Listener
self._reply = None
self._reply_event = Event()
- def _build(opspec, encoding='utf-8'):
+ def _build(self, opspec, encoding='utf-8'):
"TODO: docstring"
spec = {
'tag': _('rpc'),
'attributes': {'message-id': self._id},
'children': opspec
}
- return TreeBuilder(spec).to_string(encoding)
+ return XMLConverter(spec).to_string(encoding)
def _request(self, op):
req = self._build(op)
from ssh import SSHSession
-__all__ = ['SSHSession']
\ No newline at end of file
+__all__ = [
+ 'TransportError',
+ 'AuthenticationError',
+ 'SessionCloseError',
+ 'SSHError',
+ 'SSHUnknownHostError',
+ 'SSHSession'
+]
\ No newline at end of file
"TODO: docstrings"
-from ncclient import TransportError
+from ncclient import NCClientError
+
+class TransportError(NCClientError):
+ pass
class AuthenticationError(TransportError):
pass
from xml.etree import cElementTree as ET
from ncclient.glue import Listener
-from ncclient.content import TreeBuilder, BASE_NS
+from ncclient.content import XMLConverter, BASE_NS
from ncclient.content import qualify as _
from ncclient.content import unqualify as __
self._error_cb(err)
@staticmethod
- def build(capabilities, encoding='utf-8'):
+ def build(capabilities):
"Given a list of capability URI's returns encoded <hello> message"
spec = {
'tag': _('hello', BASE_NS),
[{ 'tag': 'capability', 'text': uri} for uri in capabilities]
}]
}
- return TreeBuilder(spec).to_string(encoding)
+ return XMLConverter(spec).to_string()
@staticmethod
def parse(raw):
from threading import Event
from Queue import Queue
-from ncclient.capabilities import Capabilities, CAPABILITIES
+from ncclient.capabilities import Capabilities
from ncclient.glue import Subject
from hello import HelloHandler
"TODO: docstring"
- def __init__(self):
+ def __init__(self, capabilities):
"Subclass constructor should call this"
Subject.__init__(self)
self.setName('session')
self._q = Queue()
- self._client_capabilities = CAPABILITIES
+ self._client_capabilities = capabilities
self._server_capabilities = None # yet
self._id = None # session-id
self._connected = False # to be set/cleared by subclass implementation
class SSHSession(Session):
- def __init__(self):
- Session.__init__(self)
+ def __init__(self, *args, **kwds):
+ Session.__init__(self, *args, **kwds)
self._host_keys = paramiko.HostKeys()
self._system_host_keys = paramiko.HostKeys()
self._transport = None