Restructcture of the directory layout
[fuel.git] / deploy / deploy.py
1 ###############################################################################
2 # Copyright (c) 2015 Ericsson AB and others.
3 # szilard.cserey@ericsson.com
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ###############################################################################
9
10
11 import os
12 import io
13 import re
14 import sys
15 import netaddr
16 import yaml
17
18 from dea import DeploymentEnvironmentAdapter
19 from dha import DeploymentHardwareAdapter
20 from install_fuel_master import InstallFuelMaster
21 from deploy_env import CloudDeploy
22 from execution_environment import ExecutionEnvironment
23
24 from common import (
25     log,
26     exec_cmd,
27     err,
28     warn,
29     check_file_exists,
30     create_dir_if_not_exists,
31     delete,
32     check_if_root,
33     ArgParser,
34 )
35
36 FUEL_VM = 'fuel'
37 PATCH_DIR = 'fuel_patch'
38 WORK_DIR = '~/deploy'
39 CWD = os.getcwd()
40
41
42 class cd:
43
44     def __init__(self, new_path):
45         self.new_path = os.path.expanduser(new_path)
46
47     def __enter__(self):
48         self.saved_path = CWD
49         os.chdir(self.new_path)
50
51     def __exit__(self, etype, value, traceback):
52         os.chdir(self.saved_path)
53
54
55 class AutoDeploy(object):
56
57     def __init__(self, no_fuel, fuel_only, no_health_check, cleanup_only,
58                  cleanup, storage_dir, pxe_bridge, iso_file, dea_file,
59                  dha_file, fuel_plugins_dir, fuel_plugins_conf_dir,
60                  no_plugins):
61         self.no_fuel = no_fuel
62         self.fuel_only = fuel_only
63         self.no_health_check = no_health_check
64         self.cleanup_only = cleanup_only
65         self.cleanup = cleanup
66         self.storage_dir = storage_dir
67         self.pxe_bridge = pxe_bridge
68         self.iso_file = iso_file
69         self.dea_file = dea_file
70         self.dha_file = dha_file
71         self.fuel_plugins_dir = fuel_plugins_dir
72         self.fuel_plugins_conf_dir = fuel_plugins_conf_dir
73         self.no_plugins = no_plugins
74         self.dea = (DeploymentEnvironmentAdapter(dea_file)
75                     if not cleanup_only else None)
76         self.dha = DeploymentHardwareAdapter(dha_file)
77         self.fuel_conf = {}
78         self.fuel_node_id = self.dha.get_fuel_node_id()
79         self.fuel_username, self.fuel_password = self.dha.get_fuel_access()
80         self.tmp_dir = None
81
82     def modify_ip(self, ip_addr, index, val):
83         ip_str = str(netaddr.IPAddress(ip_addr))
84         decimal_list = map(int, ip_str.split('.'))
85         decimal_list[index] = val
86         return '.'.join(map(str, decimal_list))
87
88     def collect_fuel_info(self):
89         self.fuel_conf['ip'] = self.dea.get_fuel_ip()
90         self.fuel_conf['gw'] = self.dea.get_fuel_gateway()
91         self.fuel_conf['dns1'] = self.dea.get_fuel_dns()
92         self.fuel_conf['netmask'] = self.dea.get_fuel_netmask()
93         self.fuel_conf['hostname'] = self.dea.get_fuel_hostname()
94         self.fuel_conf['showmenu'] = 'yes'
95
96     def install_fuel_master(self):
97         log('Install Fuel Master')
98         new_iso = ('%s/deploy-%s'
99                    % (self.tmp_dir, os.path.basename(self.iso_file)))
100         self.patch_iso(new_iso)
101         self.iso_file = new_iso
102         self.install_iso()
103
104     def install_iso(self):
105         fuel = InstallFuelMaster(self.dea_file, self.dha_file,
106                                  self.fuel_conf['ip'], self.fuel_username,
107                                  self.fuel_password, self.fuel_node_id,
108                                  self.iso_file, WORK_DIR,
109                                  self.fuel_plugins_dir, self.no_plugins)
110         fuel.install()
111
112     def patch_iso(self, new_iso):
113         tmp_orig_dir = '%s/origiso' % self.tmp_dir
114         tmp_new_dir = '%s/newiso' % self.tmp_dir
115         try:
116             self.copy(tmp_orig_dir, tmp_new_dir)
117             self.patch(tmp_new_dir, new_iso)
118         except Exception as e:
119             exec_cmd('fusermount -u %s' % tmp_orig_dir, False)
120             delete(self.tmp_dir)
121             err(e)
122
123     def copy(self, tmp_orig_dir, tmp_new_dir):
124         log('Copying...')
125         os.makedirs(tmp_orig_dir)
126         os.makedirs(tmp_new_dir)
127         exec_cmd('fuseiso %s %s' % (self.iso_file, tmp_orig_dir))
128         with cd(tmp_orig_dir):
129             exec_cmd('find . | cpio -pd %s' % tmp_new_dir)
130         exec_cmd('fusermount -u %s' % tmp_orig_dir)
131         delete(tmp_orig_dir)
132         exec_cmd('chmod -R 755 %s' % tmp_new_dir)
133
134     def patch(self, tmp_new_dir, new_iso):
135         log('Patching...')
136         patch_dir = '%s/%s' % (CWD, PATCH_DIR)
137         ks_path = '%s/ks.cfg.patch' % patch_dir
138
139         with cd(tmp_new_dir):
140             exec_cmd('cat %s | patch -p0' % ks_path)
141             delete('.rr_moved')
142             isolinux = 'isolinux/isolinux.cfg'
143             log('isolinux.cfg before: %s'
144                 % exec_cmd('grep netmask %s' % isolinux))
145             self.update_fuel_isolinux(isolinux)
146             log('isolinux.cfg after: %s'
147                 % exec_cmd('grep netmask %s' % isolinux))
148
149             iso_linux_bin = 'isolinux/isolinux.bin'
150             exec_cmd('mkisofs -quiet -r -J -R -b %s '
151                      '-no-emul-boot -boot-load-size 4 '
152                      '-boot-info-table -hide-rr-moved '
153                      '-x "lost+found:" -o %s .'
154                      % (iso_linux_bin, new_iso))
155
156     def update_fuel_isolinux(self, file):
157         with io.open(file) as f:
158             data = f.read()
159         for key, val in self.fuel_conf.iteritems():
160             pattern = r'%s=[^ ]\S+' % key
161             replace = '%s=%s' % (key, val)
162             data = re.sub(pattern, replace, data)
163         with io.open(file, 'w') as f:
164             f.write(data)
165
166     def deploy_env(self):
167         dep = CloudDeploy(self.dea, self.dha, self.fuel_conf['ip'],
168                           self.fuel_username, self.fuel_password,
169                           self.dea_file, self.fuel_plugins_conf_dir,
170                           WORK_DIR, self.no_health_check)
171         return dep.deploy()
172
173     def setup_execution_environment(self):
174         exec_env = ExecutionEnvironment(self.storage_dir, self.pxe_bridge,
175                                         self.dha_file, self.dea)
176         exec_env.setup_environment()
177
178     def cleanup_execution_environment(self):
179         exec_env = ExecutionEnvironment(self.storage_dir, self.pxe_bridge,
180                                         self.dha_file, self.dea)
181         exec_env.cleanup_environment()
182
183     def create_tmp_dir(self):
184         self.tmp_dir = '%s/fueltmp' % CWD
185         delete(self.tmp_dir)
186         create_dir_if_not_exists(self.tmp_dir)
187
188     def deploy(self):
189         self.collect_fuel_info()
190         if not self.no_fuel:
191             self.setup_execution_environment()
192             self.create_tmp_dir()
193             self.install_fuel_master()
194         if not self.fuel_only:
195             return self.deploy_env()
196         return True
197
198     def run(self):
199         check_if_root()
200         if self.cleanup_only:
201             self.cleanup_execution_environment()
202         else:
203             deploy_success = self.deploy()
204             if self.cleanup:
205                 self.cleanup_execution_environment()
206             return deploy_success
207         return True
208
209 def check_bridge(pxe_bridge, dha_path):
210     with io.open(dha_path) as yaml_file:
211         dha_struct = yaml.load(yaml_file)
212     if dha_struct['adapter'] != 'libvirt':
213         log('Using Linux Bridge %s for booting up the Fuel Master VM'
214             % pxe_bridge)
215         r = exec_cmd('ip link show %s' % pxe_bridge)
216         if pxe_bridge in r and 'state DOWN' in r:
217             err('Linux Bridge {0} is not Active, bring'
218                 ' it UP first: [ip link set dev {0} up]'.format(pxe_bridge))
219
220
221 def check_fuel_plugins_dir(dir):
222     msg = None
223     if not dir:
224         msg = 'Fuel Plugins Directory not specified!'
225     elif not os.path.isdir(dir):
226         msg = 'Fuel Plugins Directory does not exist!'
227     elif not os.listdir(dir):
228         msg = 'Fuel Plugins Directory is empty!'
229     if msg:
230         warn('%s No external plugins will be installed!' % msg)
231
232
233 def parse_arguments():
234     parser = ArgParser(prog='python %s' % __file__)
235     parser.add_argument('-nf', dest='no_fuel', action='store_true',
236                         default=False,
237                         help='Do not install Fuel Master (and Node VMs when '
238                              'using libvirt)')
239     parser.add_argument('-nh', dest='no_health_check', action='store_true',
240                         default=False,
241                         help='Don\'t run health check after deployment')
242     parser.add_argument('-fo', dest='fuel_only', action='store_true',
243                         default=False,
244                         help='Install Fuel Master only (and Node VMs when '
245                              'using libvirt)')
246     parser.add_argument('-co', dest='cleanup_only', action='store_true',
247                         default=False,
248                         help='Cleanup VMs and Virtual Networks according to '
249                              'what is defined in DHA')
250     parser.add_argument('-c', dest='cleanup', action='store_true',
251                         default=False,
252                         help='Cleanup after deploy')
253     if {'-iso', '-dea', '-dha', '-h'}.intersection(sys.argv):
254         parser.add_argument('-iso', dest='iso_file', action='store', nargs='?',
255                             default='%s/OPNFV.iso' % CWD,
256                             help='ISO File [default: OPNFV.iso]')
257         parser.add_argument('-dea', dest='dea_file', action='store', nargs='?',
258                             default='%s/dea.yaml' % CWD,
259                             help='Deployment Environment Adapter: dea.yaml')
260         parser.add_argument('-dha', dest='dha_file', action='store', nargs='?',
261                             default='%s/dha.yaml' % CWD,
262                             help='Deployment Hardware Adapter: dha.yaml')
263     else:
264         parser.add_argument('iso_file', action='store', nargs='?',
265                             default='%s/OPNFV.iso' % CWD,
266                             help='ISO File [default: OPNFV.iso]')
267         parser.add_argument('dea_file', action='store', nargs='?',
268                             default='%s/dea.yaml' % CWD,
269                             help='Deployment Environment Adapter: dea.yaml')
270         parser.add_argument('dha_file', action='store', nargs='?',
271                             default='%s/dha.yaml' % CWD,
272                             help='Deployment Hardware Adapter: dha.yaml')
273     parser.add_argument('-s', dest='storage_dir', action='store',
274                         default='%s/images' % CWD,
275                         help='Storage Directory [default: images]')
276     parser.add_argument('-b', dest='pxe_bridge', action='store',
277                         default='pxebr',
278                         help='Linux Bridge for booting up the Fuel Master VM '
279                              '[default: pxebr]')
280     parser.add_argument('-p', dest='fuel_plugins_dir', action='store',
281                         help='Fuel Plugins directory')
282     parser.add_argument('-pc', dest='fuel_plugins_conf_dir', action='store',
283                         help='Fuel Plugins Configuration directory')
284     parser.add_argument('-np', dest='no_plugins', action='store_true',
285                         default=False, help='Do not install Fuel Plugins')
286
287     args = parser.parse_args()
288     log(args)
289
290     check_file_exists(args.dha_file)
291
292     if not args.cleanup_only:
293         check_file_exists(args.dea_file)
294         check_fuel_plugins_dir(args.fuel_plugins_dir)
295
296     if not args.no_fuel and not args.cleanup_only:
297         log('Using OPNFV ISO file: %s' % args.iso_file)
298         check_file_exists(args.iso_file)
299         log('Using image directory: %s' % args.storage_dir)
300         create_dir_if_not_exists(args.storage_dir)
301         check_bridge(args.pxe_bridge, args.dha_file)
302
303     kwargs = {'no_fuel': args.no_fuel, 'fuel_only': args.fuel_only,
304               'no_health_check': args.no_health_check,
305               'cleanup_only': args.cleanup_only, 'cleanup': args.cleanup,
306               'storage_dir': args.storage_dir, 'pxe_bridge': args.pxe_bridge,
307               'iso_file': args.iso_file, 'dea_file': args.dea_file,
308               'dha_file': args.dha_file,
309               'fuel_plugins_dir': args.fuel_plugins_dir,
310               'fuel_plugins_conf_dir': args.fuel_plugins_conf_dir,
311               'no_plugins': args.no_plugins}
312     return kwargs
313
314
315 def main():
316     kwargs = parse_arguments()
317     d = AutoDeploy(**kwargs)
318     sys.exit(d.run())
319
320 if __name__ == '__main__':
321     main()