diff options
author | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2020-08-31 08:07:12 -0500 |
---|---|---|
committer | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2020-08-31 08:07:12 -0500 |
commit | 0c52c51a33a5db9dfa7f2514b7bc72e608b25ef2 (patch) | |
tree | 5db0e6448d28acb0f6176c3bdbacc20e1d5fa102 /ncserver | |
parent | bf48d49a976067cc93ecf79ac20514d3d631feb5 (diff) | |
download | netconfapk-0c52c51a33a5db9dfa7f2514b7bc72e608b25ef2.tar.gz netconfapk-0c52c51a33a5db9dfa7f2514b7bc72e608b25ef2.tar.bz2 netconfapk-0c52c51a33a5db9dfa7f2514b7bc72e608b25ef2.tar.xz netconfapk-0c52c51a33a5db9dfa7f2514b7bc72e608b25ef2.zip |
Module management system: Initial impl finished
Diffstat (limited to 'ncserver')
-rw-r--r-- | ncserver/base/modman.py | 131 |
1 files changed, 114 insertions, 17 deletions
diff --git a/ncserver/base/modman.py b/ncserver/base/modman.py index 551c1cd..1423be1 100644 --- a/ncserver/base/modman.py +++ b/ncserver/base/modman.py @@ -24,6 +24,75 @@ MODULE_SET_CHANGE_SIGNAL = Signal(('base/ModuleManager', 'module_set_changed')) yang-library-change notification should be fired, when this is supported.""" +def _import_compatible(existing, improps): + """Determine if an imported module is compatible with the existing + module that has the same name/revision. + + :param existing: + The dictionary of properties for the existing module. + + :param improps: + The dictionary of properties for the candidate module. + + :returns bool: + True if compatible, False otherwise. + """ + return (improps.keys() == existing.keys() and + all([existing[key] == improps[key] for key in existing.keys()])) + + +def _gen_modnode(parent, module): + """Generate a yanglib:module node. + + The generated node is in the NMDA format. With some massaging, it + could be used for the deprecated non-NMDA format. + + :param parent: + The parent XML node. + + :param module: + The module. + + :returns: + The generated module node. + """ + modnode = util.subelm(parent, '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)) + if getattr(module, 'M_FEATURES', None) is not None: + for feature in module.M_FEATURES: + modnode.append(util.leaf_elm('yanglib:feature', feature)) + return modnode + + +def _gen_modnode_import(parent, nodename, imname, improps): + """Generate a node for an import-only module. + + :param parent: + The parent XML node. + + :param nodename: + The name of the XML node to create. For NMDA library contents, use + 'yanglib:import-only-module'; otherwise, use 'yanglib:module'. + + :param imname: + The name of the imported module in M_IMPORTS syntax. + + :param improps: + The M_IMPORTS property dictionary for this imported module. + + :returns: + The generated module node. + """ + imn_name, imn_rev = imname.split('@') + modnode = util.subelm(parent, nodename) + modnode.append(util.leaf_elm('yanglib:name', imn_name)) + modnode.append(util.leaf_elm('yanglib:revision', imn_rev)) + modnode.append(util.leaf_elm('yanglib:namespace', improps['ns'])) + return modnode + + class ModuleManager: """Manages the modules for the NETCONF for APK Distributions server. @@ -39,9 +108,16 @@ class ModuleManager: M_NS = "urn:ietf:params:xml:ns:yang:ietf-yang-library" M_NAME = "ietf-yang-library" M_REVISION = "2019-01-04" + M_IMPORTS = {'ietf-yang-types@2013-07-15': + {'ns': "urn:ietf:params:xml:ns:yang:ietf-yang-types", + 'prefix': "yang"}, + 'ietf-inet-types@2013-07-16': + {'ns': "urn:ietf:params:xml:ns:yang:ietf-inet-types", + 'prefix': "inet"}} def __init__(self): self.modules = {'ncserver.base.modman': self} + self.imports = dict(self.M_IMPORTS) self.library = {'ncserver.base.modman': '{}@{}'.format(self.M_NAME, self.M_REVISION)} nsmap_add(self.M_PREFIX, self.M_NS) @@ -80,34 +156,47 @@ class ModuleManager: mod.M_ABI_VERSION) return None + if getattr(mod, 'M_IMPORTS', None) is not None: + for imname, improps in mod.M_IMPORTS: + if imname in self.imports: + if not _import_compatible(self.imports[imname], improps): + self.logger.error(_("Module '%s' has incompatible " + "imported module '%s'"), name, + imname) + return None + else: + self.imports[imname] = improps + 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) + MODULE_SET_CHANGE_SIGNAL.call(name, mod) 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() + def _gen_yang_library(self, node, content_id): + """Generate yanglib:yang-library node. - # NDMA-enabled yang-library node. + :param node: + The XML node to append to. + + :param content_id: + The unique Content ID for this library. + """ 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)) + _gen_modnode(modset, module) + for imname, improps in self.imports.items(): + _gen_modnode_import(modset, 'yanglib:import-only-module', imname, + improps) schema = util.subelm(lib, 'yanglib:schema') schema.append(util.leaf_elm('yanglib:name', 'apkschema')) schema.append(util.leaf_elm('yanglib:module-set', 'netconfapk')) @@ -118,19 +207,27 @@ class ModuleManager: dsnode.append(util.leaf_elm('schema', 'apkschema')) lib.append(util.leaf_elm('yanglib:content-id', content_id)) + return lib + + 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. + self._gen_yang_library(node, 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 = _gen_modnode(modstate, module) modnode.append(util.leaf_elm('yanglib:conformance-type', 'implement')) + for imname, improps in self.imports.items(): + modnode = _gen_modnode_import(modstate, 'yanglib:module', imname, + improps) + modnode.append(util.leaf_elm('yanglib:conformance-type', 'import')) def collect_running(self, node): """Collect all available information for the <running> datastore.""" |