summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPedro Alvarez <pedro.alvarez@codethink.co.uk>2014-11-09 22:50:50 +0000
committerPedro Alvarez <pedro.alvarez@codethink.co.uk>2014-11-20 18:28:23 +0000
commit272532f75b80a04e908c3b84f93ab079030dd009 (patch)
treeead99cfb72a0e62dce1956e2dd0455e1c8c8d693
parent086226c3132f87b1ab1bf7464b5da26de78de4ab (diff)
downloaddefinitions-272532f75b80a04e908c3b84f93ab079030dd009.tar.gz
Add the ability to deploy installer systems
This patch adds a baserock installer script and example of the install.conf file needed by the installer script, and a manifest file to use it in a cluster morphology for deploying an installer system. The installer script (installer.py) uses the rawdisk.write extension to deploy a baserock system to a disk. It reads from /etc/install.conf the configuration needed to install the system, but if this configuration is not present the installer scrpit will ask the information needed.
-rw-r--r--installer/etc/install.conf3
-rwxr-xr-xinstaller/installer.py178
-rw-r--r--installer/manifest3
3 files changed, 184 insertions, 0 deletions
diff --git a/installer/etc/install.conf b/installer/etc/install.conf
new file mode 100644
index 00000000..591d8c3b
--- /dev/null
+++ b/installer/etc/install.conf
@@ -0,0 +1,3 @@
+[install]
+rootfs = /rootfs
+device = /dev/sda
diff --git a/installer/installer.py b/installer/installer.py
new file mode 100755
index 00000000..aec43591
--- /dev/null
+++ b/installer/installer.py
@@ -0,0 +1,178 @@
+#!/usr/bin/python
+# Copyright (C) 2014 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 Baserock installer.'''
+
+
+
+import morphlib
+import ConfigParser
+import os
+import re
+import sys
+import json
+import subprocess
+import tempfile
+import errno
+import time
+
+class BaserockInstaller():
+
+ config_file = '/etc/install.conf'
+ to_mount = (
+ ('/proc', 'proc', 'none'),
+ ('/sys', 'sysfs', 'none'),
+
+ )
+
+ def run(self):
+ try:
+ print "Baserock installation script begins..."
+ mounted = self.do_mounts(self.to_mount)
+
+ rawdisk_path = morphlib.extensions._get_morph_extension_filename(
+ 'rawdisk', '.write')
+
+ disk_dest, rootfs = self.check_and_read_config(self.config_file)
+ self.validate_install_values(disk_dest, rootfs)
+
+ deployment_config=self.get_deployment_config(rootfs)
+
+ install_script = self.create_install_script(rawdisk_path,
+ deployment_config, rootfs, disk_dest)
+
+ self.install_system(install_script)
+ os.remove(install_script)
+ self.do_unmounts(mounted)
+ self.finish_and_reboot()
+ except BaseException, e:
+ print "Something failed, opening shell..."
+ print "Once you have finished, use `reboot -f`"
+ os.system('/bin/sh')
+
+ def validate_install_values(self, disk_dest, rootfs):
+ if not self.deploying_to_device(disk_dest):
+ print "ERROR: Not deploying to a device"
+ raise BaseException
+ if not os.path.exists(disk_dest):
+ print "ERROR: The device %s doesn't exist." % disk_dest
+ raise BaseException
+ if not self.is_baserock_rootfs(rootfs):
+ print "ERROR: The rootfs %s is not a baserock rootfs." % rootfs
+ raise BaseException
+
+ def is_baserock_rootfs(self, rootfs):
+ if os.path.isdir(os.path.join(rootfs, 'baserock')):
+ return True
+ return False
+
+ def create_install_script(self, rawdisk_path, deployment_config,
+ rootfs, disk_dest):
+ fd, script = tempfile.mkstemp()
+ with os.fdopen(fd, 'w') as fp:
+ fp.write('#!/bin/sh\n')
+ fp.write('env ')
+ for name in deployment_config:
+ if deployment_config[name] is not None:
+ fp.write('%s="%s" ' % (name, deployment_config[name]))
+ fp.write("%s %s %s\n" % (rawdisk_path, rootfs, disk_dest))
+ return script
+
+ def finish_and_reboot(self):
+ os.system("sync")
+ print "Rebooting in 5 seconds..."
+ time.sleep(5)
+ os.system("reboot -f")
+
+ def do_mounts(self, to_mount):
+ mounted = []
+ for mount_point, mount_type, source in to_mount:
+ print 'Mounting %s in %s' % (source, mount_point)
+ if not os.path.exists(mount_point):
+ os.makedirs(mount_point)
+ if self.mount( source, mount_point, mount_type) == 0:
+ mounted.append(mount_point)
+ return mounted
+
+ def mount(self, partition, mount_point, fstype=None):
+ if not fstype:
+ fstype = ''
+ else:
+ fstype = '-t %s' % fstype
+ mount_command = "mount %s %s %s" % (partition, mount_point, fstype)
+ child = subprocess.Popen(mount_command, shell=True)
+ child.communicate()[0]
+ return child.returncode
+
+ def do_unmounts(self, to_unmount):
+ for path in reversed(to_unmount):
+ print 'Unmounting %s' % path
+ unmount_command = "umount %s" % (path)
+ subprocess.Popen(unmount_command, shell=True)
+
+ def check_and_read_config(self, config_file):
+ print "Reading configuration from %s..." % config_file
+ config = ConfigParser.RawConfigParser()
+ config.read(config_file)
+ keys = ('device', 'rootfs')
+
+ device, rootfs = (self.read_option(config, 'install', key)
+ for key in keys)
+ return device, rootfs
+
+ def read_option(self, config, section, option):
+ try:
+ value = config.get(section, option)
+ except:
+ value = raw_input("Option '%s.%s' missing, please enter a value: "
+ % (section, option))
+ print "Option '%s.%s' with value '%s" % (section, option, value)
+ return value
+
+ def get_deployment_config(self, rootfs):
+ print "Reading deployment.meta of the system to install..."
+ try:
+ meta = open(os.path.join(rootfs, 'baserock/deployment.meta'))
+ except IOError as e:
+ if e.errno != errno.ENOENT:
+ raise
+ print "Failed to read deployment.meta, it will be empty"
+ deployment_config = {}
+ else:
+ deployment_config = json.load(meta).get('configuration', {})
+ meta.close()
+ print "################ Environment #################"
+ for key in deployment_config:
+ print "# %s: %s" % (key, deployment_config[key])
+ print "##############################################"
+ return deployment_config
+
+
+ def install_system(self, install_script):
+ run_script = "sh %s" % install_script
+ process = subprocess.Popen(run_script, shell=True, stdout=subprocess.PIPE)
+ for line in iter(process.stdout.readline, ''):
+ sys.stdout.write(line)
+
+ def deploying_to_device(self, location):
+ dev_regex = re.compile("^/dev/((sd|vd|mmcblk|hd)[a-z0-9]+)$")
+ if dev_regex.match(location):
+ return True
+ return False
+
+
+BaserockInstaller().run()
diff --git a/installer/manifest b/installer/manifest
new file mode 100644
index 00000000..c7c5cab3
--- /dev/null
+++ b/installer/manifest
@@ -0,0 +1,3 @@
+0100755 0 0 /installer.py
+0040755 0 0 /etc
+0100644 0 0 /etc/install.conf