shorthand = Capabilities.guess_shorthand(uri)
self._dict[uri] = shorthand
+ set = add
+
def remove(self, key):
if key in self._dict:
del self._dict[key]
if self._dict[uri] == key:
del self._dict[uri]
break
-
+
@staticmethod
def guess_shorthand(uri):
if uri.startswith('urn:ietf:params:netconf:capability:'):
return (':' + uri.split(':')[5])
+
CAPABILITIES = Capabilities([
'urn:ietf:params:netconf:base:1.0',
# See the License for the specific language governing permissions and
# limitations under the License.
-from ..error import ClientError, NETCONFError
+NETCONF_NS = 'urn:ietf:params:xml:ns:netconf:base:1.0'
-class ContentError(ClientError): pass
-
-class ValidationError(NETCONFError): pass
--- /dev/null
+# Copyright 2009 Shikhar Bhushan
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ..error import ClientError, NETCONFError
+
+class ContentError(ClientError):
+ pass
\ No newline at end of file
logging.getLogger('ncclient.content.hello')
+from . import NETCONF_NS
+from .util import qualify as _
from ..capability import Capabilities
-ns = 'urn:ietf:params:xml:ns:netconf:base:1.0'
-
def make(capabilities):
- return '<hello xmlns="%s">%s</hello>' % (ns, capabilities)
+ return '<hello xmlns="%s">%s</hello>' % (NETCONF_NS, capabilities)
def parse(raw):
id, capabilities = 0, Capabilities()
- hello = ElementTree.fromstring(raw)
- for child in hello.getchildren():
- if child.tag == '{%s}session-id' % ns:
- id = child.text
- elif child.tag == '{%s}capabilities' % ns:
- for cap in child.getiterator('{%s}capability' % ns):
- capabilities.add(cap.text)
+ root = ElementTree.fromstring(raw)
+ if root.tag == _('hello'):
+ for child in hello.getchildren():
+ if child.tag == _('session-id'):
+ id = int(child.text)
+ elif child.tag == _('capabilities'):
+ for cap in child.getiterator(_('capability')):
+ capabilities.add(cap.text)
return id, capabilities
-
-#class HelloParser:
-#
-# 'Fast parsing with expat'
-#
-# capability, sid = range(2)
-#
-# def __init__(self, raw):
-# self._sid = None
-# self._capabilities = Capabilities()
-# p = xml.parsers.expat.ParserCreate()
-# p.StartElementHandler = self._start_element
-# p.EndElementHandler = self._end_element
-# p.CharacterDataHandler = self._char_data
-# self._expect = None
-# p.parse(raw, True)
-#
-# def _start_element(self, name, attrs):
-# if name == 'capability':
-# self._expect = HelloParser.capability
-# elif name == 'session-id':
-# self._expect = HelloParser.sid
-#
-# def _end_element(self, name):
-# self._expect = None
-#
-# def _char_data(self, data):
-# if self._expect == HelloParser.capability:
-# self._capabilities.add(data)
-# elif self._expect == HelloParser.sid:
-# self._sid = int(data)
-#
-# @property
-# def sid(self): return self._sid
-#
-# @property
-# def capabilities(self): return self._capabilities
\ No newline at end of file
--- /dev/null
+# Copyright 2009 Shikhar Bhushan
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from xml.etree import cElementTree as ElementTree
+
+from . import NETCONF_NS
+from .util import qualify as _
+
+def make(id, op):
+ return '<rpc message-id="%s" xmlns="%s">%s</rpc>' % (id, NETCONF_NS, op)
+
+#def parse(raw):
+#
+# class RootElementParser:
+#
+# def __init__(self):
+# self.id = 0
+# self.is_notification = False
+#
+# def start(self, tag, attrib):
+# if tag == _('rpc'):
+# self.id = int(attrib['message-id'])
+# elif tag == _('notification'):
+# self.is_notification = True
+#
+# target = RootElementParser()
+# parser = ElementTree.XMLTreeBuilder(target=target)
+# parser.feed(raw)
+# return target.id, target.is_notification
+#
--- /dev/null
+# Copyright 2009 Shikhar Bhushan
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from . import NETCONF_NS
+
+def qualify(tag, ns=NETCONF_NS):
+ return '{%s}%s' % (ns, tag)
# See the License for the specific language governing permissions and
# limitations under the License.
-from threading import Lock
+from content import rpc
-import logging
+class SessionListener:
-logger = logging.getLogger('ncclient.listener')
-
-class Subject:
-
- def __init__(self, listeners=[]):
- self._listeners = listeners
- self._lock = Lock()
-
- def has_listener(self, listener):
- with self._lock:
- return (listener in self._listeners)
+ def __init__(self):
+ self._id2rpc = {}
+ self._subscription_id = None # notifications are delivered to the rpc
+ # that created the subscription
- def add_listener(self, listener):
- with self._lock:
- self._listeners.append(listener)
+ def set_subscription(self, id):
+ self._subscription = id
- def remove_listener(self, listener):
- with self._lock:
- try:
- self._listeners.remove(listener)
- except ValueError:
- pass
+ def reply(self, raw):
+ id, is_notification = rpc.parse(raw)
+ if is_notification:
+ self._id2rpc[self._subscription_id].event(raw)
+ else:
+ self._id2rpc[id]._deliver(raw)
+ del self._id2rpc[id]
- def dispatch(self, event, *args, **kwds):
- with self._lock:
- listeners = list(self._listeners)
- for l in listeners:
- logger.debug('dispatching [%s] to [%s]' % (event, l.__class__))
- try:
- getattr(l, event)(*args, **kwds)
- except Exception as e:
- logger.warning(e)
+ def error(self, buf):
+ pass
# See the License for the specific language governing permissions and
# limitations under the License.
-from threading import Event
+import content
-from listener import Listener
+from threading import Event
-from content import MessageIDParser
+from listener import RPCReplyListener
class RPC:
- cur_id = {}
+ current_id = {}
+ listeners = {}
def __init__(self, session=None, async=False):
self._session = None
if self._event.isSet():
return self._reply
- def do(self, session, async=False):
+ def do(self, async=False):
self._async = async
- def deliver(self, reply):
+ def _deliver(self, reply):
self._reply = reply
self._event.set()
@property
- def has_reply(self): return self._event.isSet()
+ def has_reply(self):
+ return self._event.isSet()
@property
- def async(self): return self._async
+ def is_async(self):
+ return self._async
@property
- def listener(self): return self._listener
+ def listener(self):
+ if RPC.listeners[self._sid] is None:
+ RPC.listeners[self.sid] = listener.RPCReplyListener()
+ return RPC.listeners[self._sid]
+
+ @property
+ def ok(self):
+ pass
def _next_id(self):
- cur_id[self._sid] = cur_id.get(self._sid, 0) + 1
- return cur_id[self._sid]
+ RPC.current_id[self._session.id] = RPC.current_id.get(self._session.id, 0) + 1
+ return RPC.current_id[self._sid]
class RPCReply:
- def __init__(self, raw):
+ def __init__(self, id, raw):
+ self._id = id
self._raw = raw
- def get_id(self):
- return content.rpc.parse_msg_id(raw)
-
-class RPCError(NETCONFError):
+ @property
+ def id(self):
+ return self._id
+class RPCError(NETCONFError):
pass
-
-class ReplyListener(Listener):
-
- def __init__(self):
- self._id2rpc = {}
-
- def reply(self, msg):
- reply = RPCReply(msg)
- id2rpc[reply.get_id()].deliver(reply)
-
- def error(self, buf):
- pass
# limitations under the License.
import logging
-
from threading import Thread, Event
from Queue import Queue
-from error import ClientError
-from content import hello
-from listener import Subject
from capability import CAPABILITIES
+from content import hello
+from error import ClientError
+from subject import Subject
logger = logging.getLogger('ncclient.session')
-class SessionError(ClientError): pass
+class SessionError(ClientError):
+
+ pass
class Session(Thread, Subject):
self._init_event = Event()
self._q = Queue()
- def _close(self):
- self._connected = False
-
- def _init(self):
- self._connected = True
- # start the subclass' main loop
- self.start()
- # queue client's hello message for sending
- self.send(hello.make(self._client_capabilities))
- # we expect server's hello message, wait for _init_event to be set by HelloListener
- self._init_event.wait()
- # there may have been an error
- if self._error:
- self._close()
- raise self._error
-
def connect(self):
raise NotImplementedError
### Properties
@property
- def client_capabilities(self): return self._client_capabilities
+ def client_capabilities(self):
+ return self._client_capabilities
@property
- def serve_capabilities(self): return self._server_capabilities
+ def serve_capabilities(self):
+ return self._server_capabilities
@property
- def connected(self): return self._connected
+ def connected(self):
+ return self._connected
@property
- def id(self): return self._id
+ def id(self):
+ return self._id
class HelloListener:
def close(self, err):
self._done(err)
+
+ ### Methods for which subclasses should call super after they are done
+
+ def _connect(self):
+ self._connected = True
+ # start the subclass' main loop
+ self.start()
+ # queue client's hello message for sending
+ self.send(hello.make(self._client_capabilities))
+ # we expect server's hello message, wait for _init_event to be set by HelloListener
+ self._init_event.wait()
+ # there may have been an error
+ if self._error:
+ self._close()
+ raise self._error
+
+ def _close(self):
+ self._connected = False
self._channel = transport.open_session()
self._channel.invoke_subsystem('netconf')
self._channel.set_name('netconf')
- self._init()
-
- def _close(self):
- self._channel.close()
- Session._close(self)
+ self._connect()
def run(self):
logger.debug('** broke out of main loop **')
self.dispatch('close', SessionCloseError(self._in_buf, self._out_buf))
+
+ def _close(self):
+ self._channel.close()
+ Session._close(self)
+
class MissingHostKeyPolicy(paramiko.MissingHostKeyPolicy):
def missing_host_key(self, client, hostname, key):
if not self._cb(hostname, key):
- raise SSHError
\ No newline at end of file
+ raise SSHError
--- /dev/null
+# Copyright 2009 Shikhar Bhushan
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from threading import Lock
+
+import logging
+
+logger = logging.getLogger('ncclient.listener')
+
+class Subject:
+
+ def __init__(self, listeners=[]):
+ self._listeners = listeners
+ self._lock = Lock()
+
+ def has_listener(self, listener):
+ with self._lock:
+ return (listener in self._listeners)
+
+ def add_listener(self, listener):
+ with self._lock:
+ self._listeners.append(listener)
+
+ def remove_listener(self, listener):
+ with self._lock:
+ try:
+ self._listeners.remove(listener)
+ except ValueError:
+ pass
+
+ def dispatch(self, event, *args, **kwds):
+ with self._lock:
+ listeners = list(self._listeners)
+ for l in listeners:
+ logger.debug('dispatching [%s] to [%s]' % (event, l.__class__))
+ try:
+ getattr(l, event)(*args, **kwds)
+ except Exception as e:
+ logger.warning(e)
+
+
+class SessionListener:
+
+ def __init__(self):
+ self._id2rpc = {}
+ self._subscription = None
+
+ def reply(self, raw):
+ reply = RPCReply(msg)
+ id2rpc[reply.id]._deliver(reply)
+
+ def error(self, buf):
+ pass