summaryrefslogtreecommitdiff
path: root/ncserver
diff options
context:
space:
mode:
Diffstat (limited to 'ncserver')
-rw-r--r--ncserver/base/__init__.py11
-rw-r--r--ncserver/base/modman.py143
-rw-r--r--ncserver/base/util.py18
3 files changed, 172 insertions, 0 deletions
diff --git a/ncserver/base/__init__.py b/ncserver/base/__init__.py
new file mode 100644
index 0000000..41c96db
--- /dev/null
+++ b/ncserver/base/__init__.py
@@ -0,0 +1,11 @@
+"""
+NETCONF for APK Distributions server:
+ Base functionality.
+
+Copyright © 2020 Adélie Software in the Public Benefit, Inc.
+
+Released under the terms of the NCSA license. See the LICENSE file included
+with this source distribution for more information.
+
+SPDX-License-Identifier: NCSA
+"""
diff --git a/ncserver/base/modman.py b/ncserver/base/modman.py
new file mode 100644
index 0000000..551c1cd
--- /dev/null
+++ b/ncserver/base/modman.py
@@ -0,0 +1,143 @@
+"""
+NETCONF for APK Distributions server:
+ Module Management System
+
+Copyright © 2020 Adélie Software in the Public Benefit, Inc.
+
+Released under the terms of the NCSA license. See the LICENSE file included
+with this source distribution for more information.
+
+SPDX-License-Identifier: NCSA
+"""
+
+import hashlib
+
+from logging import getLogger
+from netconf import nsmap_add, util
+from taillight import Signal
+
+from ncserver.base.util import _
+
+
+MODULE_SET_CHANGE_SIGNAL = Signal(('base/ModuleManager', 'module_set_changed'))
+"""Signal fired when the module set changes. This implies the
+yang-library-change notification should be fired, when this is supported."""
+
+
+class ModuleManager:
+ """Manages the modules for the NETCONF for APK Distributions server.
+
+ :ivar modules:
+ Dictionary of loaded modules.
+
+ :ivar library:
+ Dictionary of loaded YANG models.
+ """
+
+ M_ABI_VERSION = 1
+ M_PREFIX = "yanglib"
+ M_NS = "urn:ietf:params:xml:ns:yang:ietf-yang-library"
+ M_NAME = "ietf-yang-library"
+ M_REVISION = "2019-01-04"
+
+ def __init__(self):
+ self.modules = {'ncserver.base.modman': self}
+ self.library = {'ncserver.base.modman':
+ '{}@{}'.format(self.M_NAME, self.M_REVISION)}
+ nsmap_add(self.M_PREFIX, self.M_NS)
+ self.logger = getLogger('base/ModuleManager')
+
+ def load_module(self, name):
+ """Load a module.
+
+ :param str name:
+ The name of the module to load.
+
+ :returns:
+ Either the name of the module (if loaded), or None (on error).
+ """
+ if name in self.modules:
+ self.logger.warning(_("Module '%s' is already loaded; skipping"),
+ name)
+ return name
+
+ self.logger.debug(_("Discovering module '%s'..."), name)
+
+ mod = None
+ try:
+ mod = __import__(name, globals(), locals(), [name], 0)
+ except ImportError:
+ self.logger.error(_("Module '%s' was not found."), name)
+ return None
+
+ if any([getattr(mod, attr, None) is None
+ for attr in ['M_ABI_VERSION', 'M_NS', 'M_PREFIX', 'M_NAME']]):
+ self.logger.error(_("'%s' is not a valid module."), name)
+ return None
+
+ if mod.M_ABI_VERSION != 1:
+ self.logger.error(_("Module '%s' requires ABI version %d."), name,
+ mod.M_ABI_VERSION)
+ return None
+
+ self.logger.info(_("Loading module '%s' with ABI %d for namespace %s"),
+ name, mod.M_ABI_VERSION, mod.M_NS)
+
+ self.modules[name] = mod
+ self.library[name] = '{}@{}'.format(mod.M_NAME, mod.M_REVISION)
+ nsmap_add(mod.M_PREFIX, mod.M_NS)
+ return name
+
+ def running(self, node):
+ """Return running configuration information."""
+ # ietf-yang-library is state-only.
+
+ def operational(self, node):
+ """Return configuration and device state information."""
+ module_set_id = hashlib.sha256(','.join(self.modules.keys()).encode())
+ content_id = module_set_id.hexdigest()
+
+ # NDMA-enabled yang-library node.
+ lib = util.subelm(node, 'yanglib:yang-library')
+ modset = util.subelm(lib, 'yanglib:module-set')
+ modset.append(util.leaf_elm('yanglib:name', 'netconfapk'))
+ for module in self.modules.values():
+ # XXX or import-only-module, if it's import-only
+ modnode = util.subelm(modset, 'yanglib:module')
+ modnode.append(util.leaf_elm('yanglib:name', module.M_NAME))
+ modnode.append(util.leaf_elm('yanglib:revision',
+ module.M_REVISION))
+ modnode.append(util.leaf_elm('yanglib:namespace', module.M_NS))
+ schema = util.subelm(lib, 'yanglib:schema')
+ schema.append(util.leaf_elm('yanglib:name', 'apkschema'))
+ schema.append(util.leaf_elm('yanglib:module-set', 'netconfapk'))
+
+ for store in ['running', 'operational']:
+ dsnode = util.subelm(lib, 'yanglib:datastore')
+ dsnode.append(util.leaf_elm('name', store))
+ dsnode.append(util.leaf_elm('schema', 'apkschema'))
+
+ lib.append(util.leaf_elm('yanglib:content-id', content_id))
+
+ # Deprecated modules-state node.
+ modstate = util.subelm(node, 'yanglib:modules-state')
+ modstate.append(util.leaf_elm('yanglib:module-set-id', content_id))
+ for module in self.modules.values():
+ modnode = util.subelm(modstate, 'yanglib:module')
+ modnode.append(util.leaf_elm('yanglib:name', module.M_NAME))
+ modnode.append(util.leaf_elm('yanglib:revision',
+ module.M_REVISION))
+ modnode.append(util.leaf_elm('yanglib:namespace', module.M_NS))
+ # XXX import-only module support: use 'import'
+ modnode.append(util.leaf_elm('yanglib:conformance-type',
+ 'implement'))
+
+ def collect_running(self, node):
+ """Collect all available information for the <running> datastore."""
+ for mod in self.modules.values():
+ mod.running(node)
+
+ def collect_operational(self, node):
+ """Collect all available information for the <operational> datastore"""
+ for mod in self.modules.values():
+ mod.operational(node)
diff --git a/ncserver/base/util.py b/ncserver/base/util.py
new file mode 100644
index 0000000..3df076c
--- /dev/null
+++ b/ncserver/base/util.py
@@ -0,0 +1,18 @@
+"""
+NETCONF for APK Distributions server:
+ Base utility functions.
+
+Copyright © 2020 Adélie Software in the Public Benefit, Inc.
+
+Released under the terms of the NCSA license. See the LICENSE file included
+with this source distribution for more information.
+
+SPDX-License-Identifier: NCSA
+"""
+
+
+def _(the_string):
+ """Translate a user-facing string.
+
+ Currently not implemented, but allows us to implement translation later."""
+ return the_string