summaryrefslogtreecommitdiff
path: root/morphlib/exts/simple-network.configure
diff options
context:
space:
mode:
authorDaniel Silverstone (br7vm) <daniel.silverstone@codethink.co.uk>2013-05-17 12:55:04 +0000
committerDaniel Silverstone <daniel.silverstone@codethink.co.uk>2013-05-17 14:12:04 +0100
commit7678d348fb9337e20adeaaed47d47e28e5108dd5 (patch)
tree403583fb1e83ea4156cac947482aa03dfa9e51e3 /morphlib/exts/simple-network.configure
parent73c6a74953d9b21a0d99fe8ca95ccac0a6030aae (diff)
downloadmorph-7678d348fb9337e20adeaaed47d47e28e5108dd5.tar.gz
EXTS: simple-network.configure
Simple networking configuration extension, taking NETWORK_CONFIG from the environment and writing /etc/network/interfaces out during deployment configuration.
Diffstat (limited to 'morphlib/exts/simple-network.configure')
-rwxr-xr-xmorphlib/exts/simple-network.configure143
1 files changed, 143 insertions, 0 deletions
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()