Merge "adding files to the build cache to reduce bandwidth by reusing already downloa...
[genesis.git] / fuel / 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 import common
24
25 log = common.log
26 exec_cmd = common.exec_cmd
27 err = common.err
28 warn = common.warn
29 check_file_exists = common.check_file_exists
30 check_dir_exists = common.check_dir_exists
31 create_dir_if_not_exists = common.create_dir_if_not_exists
32 delete = common.delete
33 check_if_root = common.check_if_root
34 ArgParser = common.ArgParser
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):
60         self.no_fuel = no_fuel
61         self.fuel_only = fuel_only
62         self.no_health_check = no_health_check
63         self.cleanup_only = cleanup_only
64         self.cleanup = cleanup
65         self.storage_dir = storage_dir
66         self.pxe_bridge = pxe_bridge
67         self.iso_file = iso_file
68         self.dea_file = dea_file
69         self.dha_file = dha_file
70         self.fuel_plugins_dir = fuel_plugins_dir
71         self.dea = (DeploymentEnvironmentAdapter(dea_file)
72                     if not cleanup_only else None)
73         self.dha = DeploymentHardwareAdapter(dha_file)
74         self.fuel_conf = {}
75         self.fuel_node_id = self.dha.get_fuel_node_id()
76         self.fuel_username, self.fuel_password = self.dha.get_fuel_access()
77         self.tmp_dir = None
78
79     def modify_ip(self, ip_addr, index, val):
80         ip_str = str(netaddr.IPAddress(ip_addr))
81         decimal_list = map(int, ip_str.split('.'))
82         decimal_list[index] = val
83         return '.'.join(map(str, decimal_list))
84
85     def collect_fuel_info(self):
86         self.fuel_conf['ip'] = self.dea.get_fuel_ip()
87         self.fuel_conf['gw'] = self.dea.get_fuel_gateway()
88         self.fuel_conf['dns1'] = self.dea.get_fuel_dns()
89         self.fuel_conf['netmask'] = self.dea.get_fuel_netmask()
90         self.fuel_conf['hostname'] = self.dea.get_fuel_hostname()
91         self.fuel_conf['showmenu'] = 'yes'
92
93     def install_fuel_master(self):
94         log('Install Fuel Master')
95         new_iso = '%s/deploy-%s' \
96                   % (self.tmp_dir, os.path.basename(self.iso_file))
97         self.patch_iso(new_iso)
98         self.iso_file = new_iso
99         self.install_iso()
100
101     def install_iso(self):
102         fuel = InstallFuelMaster(self.dea_file, self.dha_file,
103                                  self.fuel_conf['ip'], self.fuel_username,
104                                  self.fuel_password, self.fuel_node_id,
105                                  self.iso_file, WORK_DIR)
106         fuel.install()
107
108     def patch_iso(self, new_iso):
109         tmp_orig_dir = '%s/origiso' % self.tmp_dir
110         tmp_new_dir = '%s/newiso' % self.tmp_dir
111         self.copy(tmp_orig_dir, tmp_new_dir)
112         self.patch(tmp_new_dir, new_iso)
113
114     def copy(self, tmp_orig_dir, tmp_new_dir):
115         log('Copying...')
116         os.makedirs(tmp_orig_dir)
117         os.makedirs(tmp_new_dir)
118         exec_cmd('fuseiso %s %s' % (self.iso_file, tmp_orig_dir))
119         with cd(tmp_orig_dir):
120             exec_cmd('find . | cpio -pd %s' % tmp_new_dir)
121         with cd(tmp_new_dir):
122             exec_cmd('fusermount -u %s' % tmp_orig_dir)
123         delete(tmp_orig_dir)
124         exec_cmd('chmod -R 755 %s' % tmp_new_dir)
125
126     def patch(self, tmp_new_dir, new_iso):
127         log('Patching...')
128         patch_dir = '%s/%s' % (CWD, PATCH_DIR)
129         ks_path = '%s/ks.cfg.patch' % patch_dir
130
131         with cd(tmp_new_dir):
132             exec_cmd('cat %s | patch -p0' % ks_path)
133             delete('.rr_moved')
134             isolinux = 'isolinux/isolinux.cfg'
135             log('isolinux.cfg before: %s'
136                 % exec_cmd('grep netmask %s' % isolinux))
137             self.update_fuel_isolinux(isolinux)
138             log('isolinux.cfg after: %s'
139                 % exec_cmd('grep netmask %s' % isolinux))
140
141             iso_linux_bin = 'isolinux/isolinux.bin'
142             exec_cmd('mkisofs -quiet -r -J -R -b %s '
143                      '-no-emul-boot -boot-load-size 4 '
144                      '-boot-info-table -hide-rr-moved '
145                      '-x "lost+found:" -o %s .'
146                      % (iso_linux_bin, new_iso))
147
148     def update_fuel_isolinux(self, file):
149         with io.open(file) as f:
150             data = f.read()
151         for key, val in self.fuel_conf.iteritems():
152             pattern = r'%s=[^ ]\S+' % key
153             replace = '%s=%s' % (key, val)
154             data = re.sub(pattern, replace, data)
155         with io.open(file, 'w') as f:
156             f.write(data)
157
158     def deploy_env(self):
159         dep = CloudDeploy(self.dea, self.dha, self.fuel_conf['ip'],
160                           self.fuel_username, self.fuel_password,
161                           self.dea_file, self.fuel_plugins_dir, WORK_DIR,
162                           self.no_health_check)
163         return dep.deploy()
164
165     def setup_execution_environment(self):
166         exec_env = ExecutionEnvironment(self.storage_dir, self.pxe_bridge,
167                                         self.dha_file, self.dea)
168         exec_env.setup_environment()
169
170     def cleanup_execution_environment(self):
171         exec_env = ExecutionEnvironment(self.storage_dir, self.pxe_bridge,
172                                         self.dha_file, self.dea)
173         exec_env.cleanup_environment()
174
175     def create_tmp_dir(self):
176         self.tmp_dir = '%s/fueltmp' % CWD
177         delete(self.tmp_dir)
178         create_dir_if_not_exists(self.tmp_dir)
179
180     def deploy(self):
181         self.collect_fuel_info()
182         if not self.no_fuel:
183             self.setup_execution_environment()
184             self.create_tmp_dir()
185             self.install_fuel_master()
186         if not self.fuel_only:
187             return self.deploy_env()
188         return True
189
190     def run(self):
191         check_if_root()
192         if self.cleanup_only:
193             self.cleanup_execution_environment()
194         else:
195             deploy_success = self.deploy()
196             if self.cleanup:
197                 self.cleanup_execution_environment()
198             return deploy_success
199         return True
200
201 def check_bridge(pxe_bridge, dha_path):
202     with io.open(dha_path) as yaml_file:
203         dha_struct = yaml.load(yaml_file)
204     if dha_struct['adapter'] != 'libvirt':
205         log('Using Linux Bridge %s for booting up the Fuel Master VM'
206             % pxe_bridge)
207         r = exec_cmd('ip link show %s' % pxe_bridge)
208         if pxe_bridge in r and 'state DOWN' in r:
209             err('Linux Bridge {0} is not Active, bring'
210                 ' it UP first: [ip link set dev {0} up]'.format(pxe_bridge))
211
212
213 def check_fuel_plugins_dir(dir):
214     msg = None
215     if not dir:
216         msg = 'Fuel Plugins Directory not specified!'
217     elif not os.path.isdir(dir):
218         msg = 'Fuel Plugins Directory does not exist!'
219     elif not os.listdir(dir):
220         msg = 'Fuel Plugins Directory is empty!'
221     if msg:
222         warn('%s Opendaylight Fuel Plugin will not be installed!' % msg)
223
224
225 def parse_arguments():
226     parser = ArgParser(prog='python %s' % __file__)
227     parser.add_argument('-nf', dest='no_fuel', action='store_true',
228                         default=False,
229                         help='Do not install Fuel Master (and Node VMs when '
230                              'using libvirt)')
231     parser.add_argument('-nh', dest='no_health_check', action='store_true',
232                         default=False,
233                         help='Don\'t run health check after deployment')
234     parser.add_argument('-fo', dest='fuel_only', action='store_true',
235                         default=False,
236                         help='Install Fuel Master only (and Node VMs when '
237                              'using libvirt)')
238     parser.add_argument('-co', dest='cleanup_only', action='store_true',
239                         default=False,
240                         help='Cleanup VMs and Virtual Networks according to '
241                              'what is defined in DHA')
242     parser.add_argument('-c', dest='cleanup', action='store_true',
243                         default=False,
244                         help='Cleanup after deploy')
245     if {'-iso', '-dea', '-dha', '-h'}.intersection(sys.argv):
246         parser.add_argument('-iso', dest='iso_file', action='store', nargs='?',
247                             default='%s/OPNFV.iso' % CWD,
248                             help='ISO File [default: OPNFV.iso]')
249         parser.add_argument('-dea', dest='dea_file', action='store', nargs='?',
250                             default='%s/dea.yaml' % CWD,
251                             help='Deployment Environment Adapter: dea.yaml')
252         parser.add_argument('-dha', dest='dha_file', action='store', nargs='?',
253                             default='%s/dha.yaml' % CWD,
254                             help='Deployment Hardware Adapter: dha.yaml')
255     else:
256         parser.add_argument('iso_file', action='store', nargs='?',
257                             default='%s/OPNFV.iso' % CWD,
258                             help='ISO File [default: OPNFV.iso]')
259         parser.add_argument('dea_file', action='store', nargs='?',
260                             default='%s/dea.yaml' % CWD,
261                             help='Deployment Environment Adapter: dea.yaml')
262         parser.add_argument('dha_file', action='store', nargs='?',
263                             default='%s/dha.yaml' % CWD,
264                             help='Deployment Hardware Adapter: dha.yaml')
265     parser.add_argument('-s', dest='storage_dir', action='store',
266                         default='%s/images' % CWD,
267                         help='Storage Directory [default: images]')
268     parser.add_argument('-b', dest='pxe_bridge', action='store',
269                         default='pxebr',
270                         help='Linux Bridge for booting up the Fuel Master VM '
271                              '[default: pxebr]')
272     parser.add_argument('-p', dest='fuel_plugins_dir', action='store',
273                         help='Fuel Plugins directory')
274
275     args = parser.parse_args()
276     log(args)
277
278     check_file_exists(args.dha_file)
279
280     if not args.cleanup_only:
281         check_file_exists(args.dea_file)
282         check_fuel_plugins_dir(args.fuel_plugins_dir)
283
284     if not args.no_fuel and not args.cleanup_only:
285         log('Using OPNFV ISO file: %s' % args.iso_file)
286         check_file_exists(args.iso_file)
287         log('Using image directory: %s' % args.storage_dir)
288         create_dir_if_not_exists(args.storage_dir)
289         check_bridge(args.pxe_bridge, args.dha_file)
290
291     kwargs = {'no_fuel': args.no_fuel, 'fuel_only': args.fuel_only,
292               'no_health_check': args.no_health_check,
293               'cleanup_only': args.cleanup_only, 'cleanup': args.cleanup,
294               'storage_dir': args.storage_dir, 'pxe_bridge': args.pxe_bridge,
295               'iso_file': args.iso_file, 'dea_file': args.dea_file,
296               'dha_file': args.dha_file,
297               'fuel_plugins_dir': args.fuel_plugins_dir}
298     return kwargs
299
300
301 def main():
302     kwargs = parse_arguments()
303
304     d = AutoDeploy(**kwargs)
305     sys.exit(d.run())
306
307 if __name__ == '__main__':
308     main()