From a752807be4d0c7487d61205d6199d8294901bbae Mon Sep 17 00:00:00 2001 From: Jeffrey Townsend Date: Sun, 27 Nov 2016 16:17:55 +0000 Subject: [PATCH] Common Loader boot-config management and manipulation. --- .../vendor-config-onl/src/bin/onl-boot-config | 4 + .../src/python/onl/bootconfig/__init__.py | 215 ++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100755 packages/base/all/vendor-config-onl/src/bin/onl-boot-config create mode 100755 packages/base/all/vendor-config-onl/src/python/onl/bootconfig/__init__.py diff --git a/packages/base/all/vendor-config-onl/src/bin/onl-boot-config b/packages/base/all/vendor-config-onl/src/bin/onl-boot-config new file mode 100755 index 00000000..63eed39c --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/bin/onl-boot-config @@ -0,0 +1,4 @@ +#!/usr/bin/python +############################################################ +from onl.bootconfig import OnlBootConfigNet +OnlBootConfigNet().main("onl-boot-config") diff --git a/packages/base/all/vendor-config-onl/src/python/onl/bootconfig/__init__.py b/packages/base/all/vendor-config-onl/src/python/onl/bootconfig/__init__.py new file mode 100755 index 00000000..f43a1610 --- /dev/null +++ b/packages/base/all/vendor-config-onl/src/python/onl/bootconfig/__init__.py @@ -0,0 +1,215 @@ +#!/usr/bin/python +############################################################ +import os +import sys +import netaddr + +class OnlBootConfig(object): + BOOT_CONFIG_DEFAULT='/mnt/onl/boot/boot-config' + + def __init__(self): + self.keys = {} + self.__classmethod("init") + + def _readf(self, fname): + with open(fname) as f: + for line in f.readlines(): + (k,d,v) = line.partition('=') + if d == '=': + self.keys[k] = v.strip() + self._original = self.keys.copy() + + def read(self, bc=None): + if bc is None: + bc = self.BOOT_CONFIG_DEFAULT + self._readf(bc) + + def set(self, k, v): + self.keys[k] = v + + def get(self, k, d=None): + return self.keys.get(k, d) + + def delete(self, k): + self.keys.pop(k, None) + + def _writeh(self, handle): + for (k, v) in self.keys.iteritems(): + handle.write("%s=%s\n" % (k, v)) + + def _writef(self, f): + with open(f, "w") as f: + self._writeh(f) + + def write(self, dst=None): + self.validate() + if dst: + self._writef(dst) + else: + from onl.mounts import OnlMountContextReadWrite + with OnlMountContextReadWrite("ONL-BOOT", logger=None): + self._writef(self.BOOT_CONFIG_DEFAULT) + + + def __classmethod(self, name, *args): + for attr in dir(self): + if attr.endswith("__%s" % name): + getattr(self, attr)(*args) + + def validate(self): + return self.__classmethod("validate") + + def argparse_init(self, ap): + ap.add_argument("--read", help="Read the given file instead of the default [ %s ]." % OnlBootConfig.BOOT_CONFIG_DEFAULT) + ap.add_argument("--write", help="Write the given file instead of the default [ %s ]." % OnlBootConfig.BOOT_CONFIG_DEFAULT) + ap.add_argument("--show", help="Show the configuration.", action='store_true') + self.__classmethod("argparse_init", ap) + ap.add_argument("--dry", help='Show changes but do not update.', action='store_true') + + def argparse_process(self, ops): + self.read(ops.read) + if(ops.show): + self._writeh(sys.stdout) + return self.__classmethod("argparse_process", ops) + + def argparse_write(self, ops): + try: + if ops.dry: + print self.keys + self.validate() + else: + self.write(ops.write) + if not ops.write and self.keys != self._original: + print "You must reboot the switch before these changes will take affect." + + except Exception, e: + print e + print "The boot configuration has not been changed." + + + def main(self, name): + import argparse + ap = argparse.ArgumentParser("name") + self.argparse_init(ap) + ops = ap.parse_args() + self.argparse_process(ops) + self.argparse_write(ops) + + +class OnlBootConfigNet(OnlBootConfig): + def __init(self): + self.netrequired = False + + def netauto_set(self): + self.delete('NETIP') + self.delete('NETMASK') + self.delete('NETGW') + self.set('NETAUTO', 'dhcp') + + def netip_set(self, addr): + self.delete('NETAUTO') + self.keys['NETIP'] = addr + + def netmask_set(self, mask): + self.delete('NETAUTO') + self.keys['NETMASK'] = mask + + def netgw_set(self, gw): + self.delete('NETAUTO') + self.keys['NETGW'] = gw + + def __validate(self): + if 'NETAUTO' not in self.keys: + + netip = self.keys.get('NETIP', None) + if netip: + if not self.is_ip_address(netip): + raise ValueError("NETIP=%s is not a valid ip-address" % (netup)) + elif self.netrequired: + raise ValueError("No IP configuration set for the management interface.") + + netmask = self.keys.get('NETMASK', None) + if netmask: + if not self.is_netmask(netmask): + raise ValueError("NETMASK=%s is not a valid netmask." % (netmask)) + elif self.netrequired: + raise ValueError("No Netmask configured for the management interface.") + + netgw = self.keys.get('NETGW', None) + if netgw: + if not self.is_ip_address(netgw): + raise ValueError("NETGW=%s is not a valid ip-address." % (netgw)) + elif self.netrequired: + raise ValueError("No gateway configured for the management interface.") + + if netip and netmask and netgw: + net = netaddr.IPNetwork("%s/%s" % (netip, netmask)) + if netaddr.IPAddress(netgw) not in net: + raise ValueError("Gateway provided is not within the management network %s" % net) + elif netip or netmask or netgw: + raise ValueError("Incomplete static network configuration. NETIP, NETMASK, and NETGW must all be set.") + + elif self.keys['NETAUTO'] != 'dhcp': + raise ValueError("The NETAUTO value '%s' is invalid." % self.keys['NETAUTO']) + + if 'NETDEV' not in self.keys: + self.keys['NETDEV'] = 'ma1' + + return True + + @staticmethod + def is_ip_address(value): + try: + netaddr.IPAddress(value) + return value + except (netaddr.core.AddrFormatError, ValueError): + return None + + @staticmethod + def is_netmask(value): + try: + if not netaddr.IPAddress(value).is_netmask(): + return False + return value + except (netaddr.core.AddrFormatError, ValueError): + return False + + @staticmethod + def argparse_type_is_ip_address(value): + if not OnlBootConfigNet.is_ip_address(value): + import argparse + raise argparse.ArgumentTypeError("%s is not a valid address." % value) + return value + + @staticmethod + def argparse_type_is_netmask(value): + if not OnlBootConfigNet.is_netmask(value): + import argparse + raise argparse.ArgumentTypeError("%s is not a valid netmask." % value) + return value + + def __argparse_init(self, ap): + ap.add_argument("--dhcp", action='store_true', help="Use DHCP on the management interface.") + ap.add_argument("--ip", help='Set static IP address for the management interface.', type=OnlBootConfigNet.argparse_type_is_ip_address) + ap.add_argument("--netmask", help='Set the static netmask for the management interface.', type=OnlBootConfigNet.argparse_type_is_netmask) + ap.add_argument("--gateway", help='Set the gateway address.', type=OnlBootConfigNet.argparse_type_is_ip_address) + + + def __argparse_process(self, ops): + if ops.dhcp: + self.netauto_set() + + if ops.ip: + self.netip_set(ops.ip) + + if ops.netmask: + self.netmask_set(ops.netmask) + + if ops.gateway: + self.netgw_set(ops.gateway) + + +if __name__ == '__main__': + bc = OnlBootConfigNet() + bc.main("onl-boot-config") +