From 7678d348fb9337e20adeaaed47d47e28e5108dd5 Mon Sep 17 00:00:00 2001 From: "Daniel Silverstone (br7vm)" Date: Fri, 17 May 2013 12:55:04 +0000 Subject: EXTS: simple-network.configure Simple networking configuration extension, taking NETWORK_CONFIG from the environment and writing /etc/network/interfaces out during deployment configuration. --- morphlib/exts/simple-network.configure | 143 +++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100755 morphlib/exts/simple-network.configure (limited to 'morphlib/exts/simple-network.configure') diff --git a/morphlib/exts/simple-network.configure b/morphlib/exts/simple-network.configure new file mode 100755 index 00000000..b98b202c --- /dev/null +++ b/morphlib/exts/simple-network.configure @@ -0,0 +1,143 @@ +#!/usr/bin/python +# Copyright (C) 2013 Codethink Limited +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +'''A Morph deployment configuration extension to handle /etc/network/interfaces + +This extension prepares /etc/network/interfaces with the interfaces specified +during deployment. + +If no network configuration is provided, eth0 will be configured for DHCP +with the hostname of the system. +''' + + +import os +import sys +import cliapp + +import morphlib + + +class SimpleNetworkError(morphlib.Error): + '''Errors associated with simple network setup''' + pass + + +class SimpleNetworkConfigurationExtension(cliapp.Application): + '''Configure /etc/network/interfaces + + Reading NETWORK_CONFIG, this extension sets up /etc/network/interfaces. + ''' + + def process_args(self, args): + network_config = os.environ.get( + "NETWORK_CONFIG", "lo:loopback;eth0:dhcp,hostname=$(hostname)") + + self.status(msg="Processing NETWORK_CONFIG=%(nc)s", nc=network_config) + + stanzas = self.parse_network_stanzas(network_config) + iface_file = self.generate_iface_file(stanzas) + + with open(os.path.join(args[0], "etc/network/interfaces"), "w") as f: + f.write(iface_file) + + def generate_iface_file(self, stanzas): + """Generate an interfaces file from the provided stanzas. + + The interfaces will be sorted by name, with loopback sorted first. + """ + + def cmp_iface_names(a, b): + a = a['name'] + b = b['name'] + if a == "lo": + return -1 + elif b == "lo": + return 1 + else: + return cmp(a,b) + + return "\n".join(self.generate_iface_stanza(stanza) + for stanza in sorted(stanzas, cmp=cmp_iface_names)) + + def generate_iface_stanza(self, stanza): + """Generate an interfaces stanza from the provided data.""" + + name = stanza['name'] + itype = stanza['type'] + lines = ["auto %s" % name, "iface %s inet %s" % (name, itype)] + lines += [" %s %s" % elem for elem in stanza['args'].items()] + lines += [""] + return "\n".join(lines) + + + def parse_network_stanzas(self, config): + """Parse a network config environment variable into stanzas. + + Network config stanzas are semi-colon separated. + """ + + return [self.parse_network_stanza(s) for s in config.split(";")] + + def parse_network_stanza(self, stanza): + """Parse a network config stanza into name, type and arguments. + + Each stanza is of the form name:type[,arg=value]... + + For example: + lo:loopback + eth0:dhcp + eth1:static,address=10.0.0.1,netmask=255.255.0.0 + """ + elements = stanza.split(",") + lead = elements.pop(0).split(":") + if len(lead) != 2: + raise SimpleNetworkError("Stanza '%s' is missing its type" % + stanza) + iface = lead[0] + iface_type = lead[1] + + if iface_type not in ['loopback', 'static', 'dhcp']: + raise SimpleNetworkError("Stanza '%s' has unknown interface type" + " '%s'" % (stanza, iface_type)) + + argpairs = [element.split("=", 1) for element in elements] + output_stanza = { "name": iface, + "type": iface_type, + "args": {} } + for argpair in argpairs: + if len(argpair) != 2: + raise SimpleNetworkError("Stanza '%s' has bad argument '%r'" + % (stanza, argpair.pop(0))) + if argpair[0] in output_stanza["args"]: + raise SimpleNetworkError("Stanza '%s' has repeated argument" + " %s" % (stanza, argpair[0])) + output_stanza["args"][argpair[0]] = argpair[1] + + return output_stanza + + def status(self, **kwargs): + '''Provide status output. + + The ``msg`` keyword argument is the actual message, + the rest are values for fields in the message as interpolated + by %. + + ''' + + self.output.write('%s\n' % (kwargs['msg'] % kwargs)) + +SimpleNetworkConfigurationExtension().run() -- cgit v1.2.1