address issue 8
[ncclient] / ncclient / manager.py
index bdc81f6..0705b9e 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+"Thin layer of abstraction around NCClient"
+
 import capabilities
 import operations
 import transport
 
-SESSION_TYPES = {
-    'ssh': transport.SSHSession
-}
+import logging
+logger = logging.getLogger('ncclient.manager')
+
+def connect_ssh(*args, **kwds):
+    """Connect to NETCONF server over SSH. See :meth:`SSHSession.connect()
+    <ncclient.transport.SSHSession.connect>` for argument details.
+
+    :rtype: :class:`Manager`
+    """
+    session = transport.SSHSession(capabilities.CAPABILITIES)
+    session.load_known_hosts()
+    session.connect(*args, **kwds)
+    return Manager(session)
+
+#: Same as :meth:`connect_ssh`
+connect = connect_ssh
 
 OPERATIONS = {
-    'get': operations.Get,
-    'get-config': operations.GetConfig,
-    'edit-config': operations.EditConfig,
-    'copy-config': operations.CopyConfig,
-    'validate': operations.Validate,
-    'commit': operations.Commit,
-    'discard-changes': operations.DiscardChanges,
-    'delete-config': operations.DeleteConfig,
-    'lock': operations.Lock,
-    'unlock': operations.Unlock,
-    'close_session': operations.CloseSession,
-    'kill-session': operations.KillSession,
+    "get": operations.Get,
+    "get_config": operations.GetConfig,
+    "edit_config": operations.EditConfig,
+    "copy_config": operations.CopyConfig,
+    "validate": operations.Validate,
+    "commit": operations.Commit,
+    "discard_changes": operations.DiscardChanges,
+    "delete_config": operations.DeleteConfig,
+    "lock": operations.Lock,
+    "unlock": operations.Unlock,
+    "close_session": operations.CloseSession,
+    "kill_session": operations.KillSession,
+    "poweroff_machine": operations.PoweroffMachine,
+    "reboot_machine": operations.RebootMachine
 }
 
-class Manager(type):
-    
-    'Facade for the API'
-    
-    def connect(self, session_type, *args, **kwds):
-        self._session = SESSION_TYPES[session_type](capabilities.CAPABILITIES)
-        self._session.connect(*args, **kwds)
-    
+class Manager(object):
+
+    """API for NETCONF operations.
+
+    It is also a context manager, so a :class:`Manager` instance can be used
+    with the *with* statement. The session is closed when the context ends. """
+
+    def __init__(self, session):
+        self._session = session
+        self._async_mode = False
+        self._timeout = None
+        self._raise_mode = 'all'
+
+    def __enter__(self):
+        return self
+
+    def __exit__(self, *argss):
+        self.close()
+        return False
+
     def __getattr__(self, name):
-        if name in OPERATIONS:
-            return OPERATIONS[name](self._session).request
-        else:
+        op = OPERATIONS.get(name, None)
+        if op is None:
             raise AttributeError
+        else:
+            return op(self.session,
+                      async=self._async_mode,
+                      timeout=self._timeout,
+                      raise_mode=self._raise_mode).request
+    
+    def locked(self, target):
+        """Returns a context manager for the *with* statement.
+
+        :arg target: name of the datastore to lock
+        :type target: `string`
+        :rtype: :class:`~ncclient.operations.LockContext`
+        """
+        return operations.LockContext(self._session, target)
+
+    def close(self):
+        """Closes the NETCONF session. First does *<close-session>* RPC."""
+        try: # try doing it clean
+            self._async_mode = False
+            self.close_session()
+        except Exception as e:
+            logger.debug('error doing <close-session> -- %r' % e)
+        if self._session.connected: # if that didn't work, this sure will :)
+            self._session.close()
+
+    @property
+    def session(self):
+        ":class:`~ncclient.transport.Session` instance"
+        return self._session
+
+    @property
+    def client_capabilities(self):
+        ":class:`~ncclient.capabilities.Capabilities` object for client"
+        return self._session._client_capabilities
+
+    @property
+    def server_capabilities(self):
+        ":class:`~ncclient.capabilities.Capabilities` object for server"
+        return self._session._server_capabilities
+
+    @property
+    def session_id(self):
+        "*<session-id>* as assigned by NETCONF server"
+        return self._session.id
+
+    @property
+    def connected(self):
+        "Whether currently connected to NETCONF server"
+        return self._session.connected
+
+    def set_async_mode(self, bool=True):
+        self._async_mode = bool
+
+    def set_raise_mode(self, mode):
+        assert(choice in ('all', 'errors', 'none'))
+        self._raise_mode = mode
+
+    async_mode = property(fget=lambda self: self._async_mode, fset=set_async_mode)
+
+    raise_mode = property(fget=set_raise_mode, fset=set_raise_mode)