Merge "Autodeploy inspired on Prototype #2"
[genesis.git] / fuel / deploy / deploy.py
1 import sys
2 import os
3 import shutil
4 import io
5 import re
6 import netaddr
7
8 from dea import DeploymentEnvironmentAdapter
9 from dha import DeploymentHardwareAdapter
10 from install_fuel_master import InstallFuelMaster
11 from deploy_env import CloudDeploy
12 import common
13
14 log = common.log
15 exec_cmd = common.exec_cmd
16 err = common.err
17 check_file_exists = common.check_file_exists
18 check_if_root = common.check_if_root
19
20 FUEL_VM = 'fuel'
21 TMP_DIR = '%s/fueltmp' % os.getenv('HOME')
22 PATCH_DIR = 'fuel_patch'
23 WORK_DIR = 'deploy'
24
25 class cd:
26     def __init__(self, new_path):
27         self.new_path = os.path.expanduser(new_path)
28
29     def __enter__(self):
30         self.saved_path = os.getcwd()
31         os.chdir(self.new_path)
32
33     def __exit__(self, etype, value, traceback):
34         os.chdir(self.saved_path)
35
36
37 class AutoDeploy(object):
38
39     def __init__(self, without_fuel, iso_file, dea_file, dha_file):
40         self.without_fuel = without_fuel
41         self.iso_file = iso_file
42         self.dea_file = dea_file
43         self.dha_file = dha_file
44         self.dea = DeploymentEnvironmentAdapter(dea_file)
45         self.dha = DeploymentHardwareAdapter(dha_file)
46         self.fuel_conf = {}
47         self.fuel_node_id = self.dha.get_fuel_node_id()
48         self.fuel_custom = self.dha.use_fuel_custom_install()
49         self.fuel_username, self.fuel_password = self.dha.get_fuel_access()
50
51     def setup_dir(self, dir):
52         self.cleanup_dir(dir)
53         os.makedirs(dir)
54
55     def cleanup_dir(self, dir):
56         if os.path.isdir(dir):
57             shutil.rmtree(dir)
58
59     def power_off_blades(self):
60         node_ids = self.dha.get_all_node_ids()
61         node_ids = list(set(node_ids) - set([self.fuel_node_id]))
62         for node_id in node_ids:
63             self.dha.node_power_off(node_id)
64
65     def modify_ip(self, ip_addr, index, val):
66         ip_str = str(netaddr.IPAddress(ip_addr))
67         decimal_list = map(int, ip_str.split('.'))
68         decimal_list[index] = val
69         return '.'.join(map(str, decimal_list))
70
71     def collect_fuel_info(self):
72         self.fuel_conf['ip'] = self.dea.get_fuel_ip()
73         self.fuel_conf['gw'] = self.dea.get_fuel_gateway()
74         self.fuel_conf['dns1'] = self.dea.get_fuel_dns()
75         self.fuel_conf['netmask'] = self.dea.get_fuel_netmask()
76         self.fuel_conf['hostname'] = self.dea.get_fuel_hostname()
77         self.fuel_conf['showmenu'] = 'yes'
78
79     def install_fuel_master(self):
80         if self.without_fuel:
81             log('Not Installing Fuel Master')
82             return
83         log('Install Fuel Master')
84         new_iso = '%s/deploy-%s' % (TMP_DIR, os.path.basename(self.iso_file))
85         self.patch_iso(new_iso)
86         self.iso_file = new_iso
87         self.install_iso()
88
89     def install_iso(self):
90         fuel = InstallFuelMaster(self.dea_file, self.dha_file,
91                                  self.fuel_conf['ip'], self.fuel_username,
92                                  self.fuel_password, self.fuel_node_id,
93                                  self.iso_file, WORK_DIR)
94         if self.fuel_custom:
95             log('Custom Fuel install')
96             fuel.custom_install()
97         else:
98             log('Ordinary Fuel install')
99             fuel.install()
100
101     def patch_iso(self, new_iso):
102         tmp_orig_dir = '%s/origiso' % TMP_DIR
103         tmp_new_dir = '%s/newiso' % TMP_DIR
104         self.copy(tmp_orig_dir, tmp_new_dir)
105         self.patch(tmp_new_dir, new_iso)
106
107     def copy(self, tmp_orig_dir, tmp_new_dir):
108         log('Copying...')
109         self.setup_dir(tmp_orig_dir)
110         self.setup_dir(tmp_new_dir)
111         exec_cmd('fuseiso %s %s' % (self.iso_file, tmp_orig_dir))
112         with cd(tmp_orig_dir):
113             exec_cmd('find . | cpio -pd %s' % tmp_new_dir)
114         with cd(tmp_new_dir):
115             exec_cmd('fusermount -u %s' % tmp_orig_dir)
116         shutil.rmtree(tmp_orig_dir)
117         exec_cmd('chmod -R 755 %s' % tmp_new_dir)
118
119     def patch(self, tmp_new_dir, new_iso):
120         log('Patching...')
121         patch_dir = '%s/%s' % (os.getcwd(), PATCH_DIR)
122         ks_path = '%s/ks.cfg.patch' % patch_dir
123
124         with cd(tmp_new_dir):
125             exec_cmd('cat %s | patch -p0' % ks_path)
126             shutil.rmtree('.rr_moved')
127             isolinux = 'isolinux/isolinux.cfg'
128             log('isolinux.cfg before: %s'
129                 % exec_cmd('grep netmask %s' % isolinux))
130             self.update_fuel_isolinux(isolinux)
131             log('isolinux.cfg after: %s'
132                 % exec_cmd('grep netmask %s' % isolinux))
133
134             iso_linux_bin = 'isolinux/isolinux.bin'
135             exec_cmd('mkisofs -quiet -r -J -R -b %s '
136                      '-no-emul-boot -boot-load-size 4 '
137                      '-boot-info-table -hide-rr-moved '
138                      '-x "lost+found:" -o %s .'
139                      % (iso_linux_bin, new_iso))
140
141     def update_fuel_isolinux(self, file):
142         with io.open(file) as f:
143             data = f.read()
144         for key, val in self.fuel_conf.iteritems():
145             pattern = r'%s=[^ ]\S+' % key
146             replace = '%s=%s' % (key, val)
147             data = re.sub(pattern, replace, data)
148         with io.open(file, 'w') as f:
149             f.write(data)
150
151     def deploy_env(self):
152         dep = CloudDeploy(self.dha, self.fuel_conf['ip'], self.fuel_username,
153                           self.fuel_password, self.dea_file, WORK_DIR)
154         dep.deploy()
155
156     def deploy(self):
157         check_if_root()
158         self.setup_dir(TMP_DIR)
159         self.collect_fuel_info()
160         self.power_off_blades()
161         self.install_fuel_master()
162         self.cleanup_dir(TMP_DIR)
163         self.deploy_env()
164
165 def usage():
166     print '''
167     Usage:
168     python deploy.py [-nf] <isofile> <deafile> <dhafile>
169
170     Optional arguments:
171       -nf   Do not install Fuel master
172     '''
173
174 def parse_arguments():
175     if (len(sys.argv) < 4 or len(sys.argv) > 5
176         or (len(sys.argv) == 5 and sys.argv[1] != '-nf')):
177         log('Incorrect number of arguments')
178         usage()
179         sys.exit(1)
180     without_fuel = False
181     if len(sys.argv) == 5 and sys.argv[1] == '-nf':
182         without_fuel = True
183     iso_file = sys.argv[-3]
184     dea_file = sys.argv[-2]
185     dha_file = sys.argv[-1]
186     check_file_exists(iso_file)
187     check_file_exists(dea_file)
188     check_file_exists(dha_file)
189     return (without_fuel, iso_file, dea_file, dha_file)
190
191 def main():
192
193     without_fuel, iso_file, dea_file, dha_file = parse_arguments()
194
195     d = AutoDeploy(without_fuel, iso_file, dea_file, dha_file)
196     d.deploy()
197
198 if __name__ == '__main__':
199     main()