Updates docs for SR1 with final revision
[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                                  self.fuel_plugins_dir)
107         fuel.install()
108
109     def patch_iso(self, new_iso):
110         tmp_orig_dir = '%s/origiso' % self.tmp_dir
111         tmp_new_dir = '%s/newiso' % self.tmp_dir
112         self.copy(tmp_orig_dir, tmp_new_dir)
113         self.patch(tmp_new_dir, new_iso)
114
115     def copy(self, tmp_orig_dir, tmp_new_dir):
116         log('Copying...')
117         os.makedirs(tmp_orig_dir)
118         os.makedirs(tmp_new_dir)
119         exec_cmd('fuseiso %s %s' % (self.iso_file, tmp_orig_dir))
120         with cd(tmp_orig_dir):
121             exec_cmd('find . | cpio -pd %s' % tmp_new_dir)
122         with cd(tmp_new_dir):
123             exec_cmd('fusermount -u %s' % tmp_orig_dir)
124         delete(tmp_orig_dir)
125         exec_cmd('chmod -R 755 %s' % tmp_new_dir)
126
127     def patch(self, tmp_new_dir, new_iso):
128         log('Patching...')
129         patch_dir = '%s/%s' % (CWD, PATCH_DIR)
130         ks_path = '%s/ks.cfg.patch' % patch_dir
131
132         with cd(tmp_new_dir):
133             exec_cmd('cat %s | patch -p0' % ks_path)
134             delete('.rr_moved')
135             isolinux = 'isolinux/isolinux.cfg'
136             log('isolinux.cfg before: %s'
137                 % exec_cmd('grep netmask %s' % isolinux))
138             self.update_fuel_isolinux(isolinux)
139             log('isolinux.cfg after: %s'
140                 % exec_cmd('grep netmask %s' % isolinux))
141
142             iso_linux_bin = 'isolinux/isolinux.bin'
143             exec_cmd('mkisofs -quiet -r -J -R -b %s '
144                      '-no-emul-boot -boot-load-size 4 '
145                      '-boot-info-table -hide-rr-moved '
146                      '-x "lost+found:" -o %s .'
147                      % (iso_linux_bin, new_iso))
148
149     def update_fuel_isolinux(self, file):
150         with io.open(file) as f:
151             data = f.read()
152         for key, val in self.fuel_conf.iteritems():
153             pattern = r'%s=[^ ]\S+' % key
154             replace = '%s=%s' % (key, val)
155             data = re.sub(pattern, replace, data)
156         with io.open(file, 'w') as f:
157             f.write(data)
158
159     def deploy_env(self):
160         dep = CloudDeploy(self.dea, self.dha, self.fuel_conf['ip'],
161                           self.fuel_username, self.fuel_password,
162                           self.dea_file, WORK_DIR, 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 No external plugins will 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()