summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorA. Wilcox <AWilcox@Wilcox-Tech.com>2020-10-22 14:26:20 -0500
committerA. Wilcox <AWilcox@Wilcox-Tech.com>2020-10-22 14:26:20 -0500
commit47ed12959dc1ac3d65b211f467ce23408fe36c48 (patch)
tree477eea756170953539fc95d000658c13a544f622
parent9eafe63c60759d9da25bfb10b927fb0699e3fd7c (diff)
downloadnetconfapk-47ed12959dc1ac3d65b211f467ce23408fe36c48.tar.gz
netconfapk-47ed12959dc1ac3d65b211f467ce23408fe36c48.tar.bz2
netconfapk-47ed12959dc1ac3d65b211f467ce23408fe36c48.tar.xz
netconfapk-47ed12959dc1ac3d65b211f467ce23408fe36c48.zip
Implement module RPCs and RPC logging
-rw-r--r--doc/roadmap.rst26
-rw-r--r--ncserver/base/modman.py12
-rw-r--r--ncserver/module/system.py17
-rw-r--r--ncserver/server.py36
4 files changed, 77 insertions, 14 deletions
diff --git a/doc/roadmap.rst b/doc/roadmap.rst
index f5c1957..d708a3b 100644
--- a/doc/roadmap.rst
+++ b/doc/roadmap.rst
@@ -22,7 +22,7 @@
* [X] Respond to state requests
-* [ ] Handle per-module RPC requests
+* [X] Handle per-module RPC requests
The module management system will be the heart of ``ncserver``. Similar to
@@ -60,36 +60,36 @@ Resolved TBDs
-[/] Logging system
+[X] Logging system
------------------
* [X] Output format
* [X] Configuration
-* [/] Event logging
+* [X] Event logging
- * [/] auth
+ * [X] auth
- * [ ] Connection established
+ * [X] Connection established
* [X] Authentication succeess
* [X] Authentication failure
- * [ ] Connection closed
+ * [X] Connection closed
- * [ ] operational
+ * [X] operational
- * [ ] RPC executed
+ * [X] RPC executed
- * [ ] RPC unknown
+ * [X] RPC unknown
- * [ ] RPC failure
+ * [X] RPC failure
- * [ ] RPC success
+ * [X] RPC success
- * [/] config
+ * [X] config
* [X] Configuration editing
@@ -97,7 +97,7 @@ Resolved TBDs
* [X] Configuration reading
- * [ ] Configuration operation denied
+ * [X] Configuration operation denied
This system is reponsible for all event logging for the NETCONF APK
project. See the `logging specification`_ for more details.
diff --git a/ncserver/base/modman.py b/ncserver/base/modman.py
index 3bf8fcb..a47fa3e 100644
--- a/ncserver/base/modman.py
+++ b/ncserver/base/modman.py
@@ -127,6 +127,7 @@ class ModuleManager:
self.imports = dict(self.M_IMPORTS)
self.library = {'ncserver.base.modman':
'{}@{}'.format(self.M_NAME, self.M_REVISION)}
+ self.rpcs = dict()
nsmap_add(self.M_PREFIX, self.M_NS)
self.logger = getLogger('base/ModuleManager')
@@ -195,6 +196,9 @@ class ModuleManager:
else:
self.imports[imname] = improps
+ if getattr(mod, 'M_RPCS', None) is not None:
+ self.rpcs.update(mod.M_RPCS)
+
self.logger.info(_("Loading module '%s' with ABI %d for namespace %s"),
name, mod.M_ABI_VERSION, mod.M_NS)
@@ -235,6 +239,14 @@ class ModuleManager:
return capabs
+ def has_rpc(self, name: str) -> bool:
+ """Determine if an RPC is implemented by a module."""
+ return name in self.rpcs
+
+ def rpc(self, name: str):
+ """Return an RPC that is implemented by a module."""
+ return self.rpcs.get(name, None)
+
def running(self, node):
"""Return running configuration information."""
# ietf-yang-library is state-only.
diff --git a/ncserver/module/system.py b/ncserver/module/system.py
index 3898576..c27b3a0 100644
--- a/ncserver/module/system.py
+++ b/ncserver/module/system.py
@@ -522,3 +522,20 @@ def edit(session, rpc, node, def_op):
methods[name](session, rpc, subsystem, def_op)
else:
raise error.UnknownElementAppError(rpc, subsystem)
+
+
+def rpc_system_shutdown(session, rpc, *params):
+ """Shutdown the system."""
+ subprocess.Popen(["/sbin/poweroff"])
+ return util.elm('nc:ok')
+
+
+def rpc_system_restart(session, rpc, *params):
+ """Restart the system."""
+ subprocess.Popen(["/sbin/reboot"])
+ return util.elm('nc:ok')
+
+
+M_RPCS = {'system-shutdown': rpc_system_shutdown,
+ 'system-restart': rpc_system_restart}
+"""The RPCs implemented by this module."""
diff --git a/ncserver/server.py b/ncserver/server.py
index f081217..796e85d 100644
--- a/ncserver/server.py
+++ b/ncserver/server.py
@@ -20,7 +20,8 @@ import paramiko
from netconf import error, util
from netconf.server import NetconfSSHServer, SSHAuthorizedKeysController
-from ncserver.base.log import configure_logging, LOG_AUTH, LOG_CONFIG
+from ncserver.base.log import configure_logging, LOG_AUTH, LOG_CONFIG, \
+ LOG_OPERATIONAL
from ncserver.base.modman import ModuleManager
from ncserver.base.util import user_for_session
from ncserver.config import ConfigManager
@@ -100,6 +101,12 @@ class Server:
for module in self.config.get_list('server', 'modules'):
self.modman.load_module(module)
+ def __getattr__(self, attr):
+ """Maybe pass RPC calls on."""
+ if attr.startswith("rpc_"):
+ return self._rpc_wrapper
+ raise AttributeError("'Server' object has no attribute '" + attr + "'")
+
def close(self):
"""Close all connections."""
self.server.close()
@@ -117,6 +124,33 @@ class Server:
for capab in our_capabs:
util.subelm(capabilities, 'capability').text = capab
+ def _rpc_wrapper(self, session, rpc, *params):
+ """Handle module RPCs."""
+ name = QName(rpc[0].tag).localname
+ LOG_OPERATIONAL.info('RPC %s invoked by %s on session %d',
+ name, user_for_session(session), session.session_id)
+ if self.modman.has_rpc(name):
+ try:
+ result = self.modman.rpc(name)(session, rpc, *params)
+ LOG_OPERATIONAL.info(
+ 'RPC %s invoked by %s on session %d completed',
+ name, user_for_session(session), session.session_id
+ )
+ return result
+ except error.RPCServerError as rpc_error:
+ LOG_OPERATIONAL.error(
+ 'RPC %s invoked by %s on session %d encountered error %s',
+ name, user_for_session(session), session.session_id,
+ rpc_error
+ )
+ raise
+ else:
+ LOG_OPERATIONAL.warning(
+ 'RPC %s invoked by %s on session %d is unknown',
+ name, user_for_session(session), session.session_id
+ )
+ raise error.OperationNotSupportedProtoError(rpc)
+
def rpc_get(self, session, rpc, filter_or_none): # pylint: disable=W0613
"""Handle the <get/> RPC."""
log_read(session, rpc)