Autodeployer support for ODL Plugin installation + Fuel 6.1
[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     parser.add_argument('-iso', dest='iso_file', action='store', nargs='?',
246                         default='%s/OPNFV.iso' % CWD,
247                         help='ISO File [default: OPNFV.iso]')
248     parser.add_argument('-dea', dest='dea_file', action='store', nargs='?',
249                         default='%s/dea.yaml' % CWD,
250                         help='Deployment Environment Adapter: dea.yaml')
251     parser.add_argument('-dha', dest='dha_file', action='store', nargs='?',
252                         default='%s/dha.yaml' % CWD,
253                         help='Deployment Hardware Adapter: dha.yaml')
254     parser.add_argument('-s', dest='storage_dir', action='store',
255                         default='%s/images' % CWD,
256                         help='Storage Directory [default: images]')
257     parser.add_argument('-b', dest='pxe_bridge', action='store',
258                         default='pxebr',
259                         help='Linux Bridge for booting up the Fuel Master VM '
260                              '[default: pxebr]')
261     parser.add_argument('-p', dest='fuel_plugins_dir', action='store',
262                         help='Fuel Plugins directory')
263
264     args = parser.parse_args()
265     log(args)
266
267     check_file_exists(args.dha_file)
268
269     if not args.cleanup_only:
270         check_file_exists(args.dea_file)
271         check_fuel_plugins_dir(args.fuel_plugins_dir)
272
273     if not args.no_fuel and not args.cleanup_only:
274         log('Using OPNFV ISO file: %s' % args.iso_file)
275         check_file_exists(args.iso_file)
276         log('Using image directory: %s' % args.storage_dir)
277         create_dir_if_not_exists(args.storage_dir)
278         check_bridge(args.pxe_bridge, args.dha_file)
279
280     kwargs = {'no_fuel': args.no_fuel, 'fuel_only': args.fuel_only,
281               'no_health_check': args.no_health_check,
282               'cleanup_only': args.cleanup_only, 'cleanup': args.cleanup,
283               'storage_dir': args.storage_dir, 'pxe_bridge': args.pxe_bridge,
284               'iso_file': args.iso_file, 'dea_file': args.dea_file,
285               'dha_file': args.dha_file,
286               'fuel_plugins_dir': args.fuel_plugins_dir}
287     return kwargs
288
289
290 def main():
291     kwargs = parse_arguments()
292
293     d = AutoDeploy(**kwargs)
294     sys.exit(d.run())
295
296 if __name__ == '__main__':
297     main()