summaryrefslogtreecommitdiff
path: root/installer/bin/installer.py
blob: aec43591e73b6fa0d9ab7effdacdfb4860ad7381 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
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()