#!/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()