diff options
author | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2020-10-23 20:22:19 -0500 |
---|---|---|
committer | A. Wilcox <AWilcox@Wilcox-Tech.com> | 2020-10-23 20:22:19 -0500 |
commit | 1ae754a07a38be3a524774c42928aa4e13801d71 (patch) | |
tree | 81b5911a847e630f6d3bdc52482d2e4c8b04f093 /ncserver | |
parent | 145878b97d008b255251c7fc4eb4e1e2aa5816da (diff) | |
download | netconfapk-1ae754a07a38be3a524774c42928aa4e13801d71.tar.gz netconfapk-1ae754a07a38be3a524774c42928aa4e13801d71.tar.bz2 netconfapk-1ae754a07a38be3a524774c42928aa4e13801d71.tar.xz netconfapk-1ae754a07a38be3a524774c42928aa4e13801d71.zip |
ietf-system: Support read of NTPsec configuration
Diffstat (limited to 'ncserver')
-rw-r--r-- | ncserver/module/system.py | 156 |
1 files changed, 154 insertions, 2 deletions
diff --git a/ncserver/module/system.py b/ncserver/module/system.py index c27b3a0..d5c097c 100644 --- a/ncserver/module/system.py +++ b/ncserver/module/system.py @@ -85,15 +85,165 @@ _LOCATION_PATH = '/etc/netconf/location.txt' """The file path for the contents of /sys:system/location.""" +class NTPServer: # pylint: disable=R0902 + """Represents an NTP server entry.""" + def __init__(self, host: str, **kwargs): + """Create a new NTP server entry. + + :param str host: + The host, in IP or DNS form. + """ + self.host = host + self.mode = kwargs.pop('mode', 'pool') + if self.mode in ('server', 'peer'): + self.key = kwargs.pop('key', None) + else: + self.key = None + self.burst = kwargs.pop('burst', False) + self.iburst = kwargs.pop('iburst', False) + self.version = kwargs.pop('version', None) + self.prefer = kwargs.pop('prefer', False) + self.minpoll = kwargs.pop('minpoll', None) + self.maxpoll = kwargs.pop('maxpoll', None) + if self.mode == 'poll': + self.preempt = kwargs.pop('preempt', False) + else: + self.preempt = False + + def __repr__(self): + """Return a representation of this object.""" + args = {'mode': '"' + self.mode + '"'} + if self.burst: + args['burst'] = True + if self.iburst: + args['iburst'] = True + if self.version is not None: + args['version'] = '"' + self.version + '"' + if self.prefer: + args['prefer'] = True + if self.minpoll is not None: + args['minpoll'] = '"' + self.minpoll + '"' + if self.maxpoll is not None: + args['maxpoll'] = '"' + self.maxpoll + '"' + + if self.mode in ('server', 'peer') and self.key is not None: + args['key'] = '"' + self.key + '"' + if self.mode == 'poll' and self.preempt: + args['preempt'] = True + + nondef_args = ', '.join( + ['{}={}'.format(*item) for item in args.items()] + ) + return "NTPServer('{host}', {nondef_args})".format(host=self.host, + nondef_args=nondef_args) + + def __str__(self): + """Return the configuration line for this server.""" + line = self.mode + ' ' + + if ':' in self.host and '[' not in self.host: + if '%' in self.host: + addr, dev = self.host.split('%') + line += '[' + addr + ']%' + dev + else: + line += '[' + self.host + ']' + else: + line += self.host + + if self.mode in ('server', 'peer') and self.key is not None: + line += ' key ' + self.key + + if self.mode in ('pool', 'server'): + if self.burst: + line += ' burst' + if self.iburst: + line += ' iburst' + + if self.version is not None: + line += ' version ' + str(self.version) + + if self.prefer: + line += ' prefer' + + if self.minpoll is not None: + line += ' minpoll ' + self.minpoll + if self.maxpoll is not None: + line += ' maxpoll ' + self.maxpoll + + if self.mode == 'pool' and self.preempt: + line += ' preempt' + + return line + + @classmethod + def from_line(cls, line: str): + """Create an NTPServer object from a configuration file line.""" + tokens = line.strip().split(' ') + tokens.reverse() + + kwargs = {'mode': tokens.pop()} + host = tokens.pop() + + bool_keys = ('burst', 'iburst', 'prefer', 'preempt') + value_keys = ('key', 'version', 'minpoll', 'maxpoll') + + while len(tokens) > 0: + key = tokens.pop() + if key in bool_keys: + kwargs[key] = True + elif key in value_keys: + kwargs[key] = tokens.pop() + else: + LOGGER.warning('unknown NTP server conf key: %s', key) + + return cls(host, **kwargs) + + +def _parse_ntp_config() -> dict: + """Parse /etc/ntp.conf and return the configuration. + + The return value is a dictionary with the following keys: + + * servers: a list of NTPServer objects. + """ + conf = open('/etc/ntp.conf', 'r') + lines = conf.readlines() + conf.close() + + result = {'servers': list()} + + for line in lines: + line.strip() + if line.startswith('#') or len(line) == 0: + continue + if any([line.startswith(word) for word in ('server', 'pool', 'peer')]): + result['servers'].append(NTPServer.from_line(line)) + else: + LOGGER.warning('unhandled NTP configuration line: %s', line) + + return result + + def _parse_ntp_conf_to(sys_node): """Parse NTP configuration into /sys:system/ntp format.""" root = util.subelm(sys_node, 'sys:ntp') - if not os.path.exists('/etc/ntpsec/ntp.conf'): + if not os.path.exists('/etc/ntp.conf'): root.append(util.leaf_elm('sys:enabled', 'false')) return - # XXX TODO: parse NTP configuration. + conf = _parse_ntp_config() + for server in conf['servers']: + serv_node = util.subelm(root, 'sys:server') + serv_node.append(util.leaf_elm('sys:name', server.host)) + serv_node.append(util.leaf_elm('sys:association-type', server.mode)) + serv_node.append(util.leaf_elm('sys:iburst', + str(server.iburst).lower())) + serv_node.append(util.leaf_elm('sys:prefer', + str(server.prefer).lower())) + udp = util.subelm(serv_node, 'sys:udp') + udp.append(util.leaf_elm('sys:address', server.host)) + return def _parse_resolv_conf() -> dict: @@ -318,6 +468,8 @@ def _edit_dns_search(session, rpc, node, def_op, curr: dict): def _edit_dns_server(session, rpc, node, def_op, curr: dict): + # yes, this function is too complex. + # pylint: disable=R0912,R0914,R0915 """Edit DNS nameserver configuration.""" operation = node_operation(node, def_op) xmlmap = {'': 'urn:ietf:params:xml:ns:yang:ietf-system'} |