Restructcture of the directory layout
[fuel.git] / deploy / reap.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 time
12 import os
13 import yaml
14 import glob
15 import shutil
16
17 from common import (
18     N,
19     E,
20     R,
21     ArgParser,
22     exec_cmd,
23     parse,
24     err,
25     log,
26     delete,
27     commafy,
28 )
29
30 DEA_1 = '''
31 title: Deployment Environment Adapter (DEA)
32 # DEA API version supported
33 version: 1.1
34 created: {date}
35 comment: {comment}
36 '''
37
38 DHA_1 = '''
39 title: Deployment Hardware Adapter (DHA)
40 # DHA API version supported
41 version: 1.1
42 created: {date}
43 comment: {comment}
44
45 # Adapter to use for this definition
46 # adapter: [ipmi|libvirt]
47 adapter:
48
49 # Node list.
50 # Mandatory properties are id and role.
51 # All other properties are adapter specific.
52 # For Non-Fuel nodes controlled by:
53 #   - ipmi adapter you need to provide:
54 #       pxeMac
55 #       ipmiIp
56 #       ipmiUser
57 #       ipmiPass
58 #   - libvirt adapter you need to provide:
59 #       libvirtName: <whatever>
60 #       libvirtTemplate: [libvirt/vms/controller.xml | libvirt/vms/compute.xml]
61 #
62 # For the Fuel Node you need to provide:
63 #       libvirtName: <whatever>
64 #       libvirtTemplate: libvirt/vms/fuel.xml
65 #       isFuel: yes
66 #       username: root
67 #       password: r00tme
68 '''
69
70 DHA_2 = '''
71 # Adding the Fuel node as node id {node_id}
72 # which may not be correct - please adjust as needed.
73 '''
74
75 DISKS = {'fuel': '30G',
76          'controller': '30G',
77          'compute': '30G'}
78
79
80 class Reap(object):
81
82     def __init__(self, dea_file, dha_file, comment):
83         self.dea_file = dea_file
84         self.dha_file = dha_file
85         self.comment = comment
86         self.temp_dir = None
87         self.env = None
88         self.env_id = None
89         self.last_node = None
90
91     def get_env(self):
92         env_list = parse(exec_cmd('fuel env'))
93         if len(env_list) > 1:
94             err('Not exactly one environment')
95         self.env = env_list[0]
96         self.env_id = self.env[E['id']]
97
98     def download_config(self, config_type):
99         log('Download %s config for environment %s'
100             % (config_type, self.env_id))
101         exec_cmd('fuel %s --env %s --download --dir %s'
102                  % (config_type, self.env_id, self.temp_dir))
103
104     def write(self, file, text, newline=True):
105         mode = 'a' if os.path.isfile(file) else 'w'
106         with open(file, mode) as f:
107             f.write('%s%s' % (text, ('\n' if newline else '')))
108
109     def write_yaml(self, file, data, newline=True):
110         self.write(file, yaml.dump(data, default_flow_style=False).strip(),
111                    newline)
112
113     def get_node_by_id(self, node_list, node_id):
114         for node in node_list:
115             if node[N['id']] == node_id:
116                 return node
117
118     def reap_interface(self, node_id, interfaces):
119         interface, mac = self.get_interface(node_id)
120         if_name = None
121         if interfaces:
122             if_name = self.check_dict_exists(interfaces, interface)
123         if not if_name:
124             if_name = 'interfaces_%s' % str(len(interfaces) + 1)
125             interfaces[if_name] = interface
126         return if_name, mac
127
128     def reap_transformation(self, node_id, roles, transformations):
129         main_role = 'controller' if 'controller' in roles else 'compute'
130         node_file = glob.glob('%s/deployment_%s/*%s_%s.yaml'
131                               % (self.temp_dir, self.env_id,
132                                  main_role, node_id))
133         tr_name = None
134         with open(node_file[0]) as f:
135             node_config = yaml.load(f)
136         transformation = {'transformations':
137                               node_config['network_scheme']['transformations']}
138         if transformations:
139             tr_name = self.check_dict_exists(transformations, transformation)
140         if not tr_name:
141             tr_name = 'transformations_%s' % str(len(transformations) + 1)
142             transformations[tr_name] = transformation
143         return tr_name
144
145     def check_dict_exists(self, main_dict, dict):
146         for key, val in main_dict.iteritems():
147             if cmp(dict, val) == 0:
148                 return key
149
150     def reap_nodes_interfaces_transformations(self):
151         node_list = parse(exec_cmd('fuel node'))
152         real_node_ids = [node[N['id']] for node in node_list]
153         real_node_ids.sort()
154         min_node = real_node_ids[0]
155
156         interfaces = {}
157         transformations = {}
158         dea_nodes = []
159         dha_nodes = []
160
161         for real_node_id in real_node_ids:
162             node_id = int(real_node_id) - int(min_node) + 1
163             self.last_node = node_id
164             node = self.get_node_by_id(node_list, real_node_id)
165             roles = commafy(node[N['roles']])
166             if not roles:
167                 err('Fuel Node %s has no role' % real_node_id)
168             dea_node = {'id': node_id,
169                         'role': roles}
170             dha_node = {'id': node_id}
171             if_name, mac = self.reap_interface(real_node_id, interfaces)
172             tr_name = self.reap_transformation(real_node_id, roles,
173                                                transformations)
174             dea_node.update(
175                 {'interfaces': if_name,
176                  'transformations': tr_name})
177
178             dha_node.update(
179                 {'pxeMac': mac if mac else None,
180                  'ipmiIp': None,
181                  'ipmiUser': None,
182                  'ipmiPass': None,
183                  'libvirtName': None,
184                  'libvirtTemplate': None})
185
186             dea_nodes.append(dea_node)
187             dha_nodes.append(dha_node)
188
189         self.write_yaml(self.dha_file, {'nodes': dha_nodes}, False)
190         self.write_yaml(self.dea_file, {'nodes': dea_nodes})
191         self.write_yaml(self.dea_file, interfaces)
192         self.write_yaml(self.dea_file, transformations)
193         self.reap_fuel_node_info()
194         self.write_yaml(self.dha_file, {'disks': DISKS})
195
196     def reap_fuel_node_info(self):
197         dha_nodes = []
198         dha_node = {
199             'id': self.last_node + 1,
200             'libvirtName': None,
201             'libvirtTemplate': None,
202             'isFuel': True,
203             'username': 'root',
204             'password': 'r00tme'}
205
206         dha_nodes.append(dha_node)
207
208         self.write(self.dha_file, DHA_2.format(node_id=dha_node['id']), False)
209         self.write_yaml(self.dha_file, dha_nodes)
210
211     def reap_environment_info(self):
212         network_file = ('%s/network_%s.yaml'
213                         % (self.temp_dir, self.env_id))
214         network = self.read_yaml(network_file)
215         env = {'environment':
216                    {'name': self.env[E['name']],
217                     'mode': self.env[E['mode']],
218                     'net_segment_type':
219                         network['networking_parameters']['segmentation_type']}}
220         self.write_yaml(self.dea_file, env)
221         wanted_release = None
222         rel_list = parse(exec_cmd('fuel release'))
223         for rel in rel_list:
224             if rel[R['id']] == self.env[E['release_id']]:
225                 wanted_release = rel[R['name']]
226         self.write_yaml(self.dea_file, {'wanted_release': wanted_release})
227
228     def reap_fuel_settings(self):
229         data = self.read_yaml('/etc/fuel/astute.yaml')
230         fuel = {}
231         del data['ADMIN_NETWORK']['mac']
232         del data['ADMIN_NETWORK']['interface']
233         for key in ['ADMIN_NETWORK', 'HOSTNAME', 'DNS_DOMAIN', 'DNS_SEARCH',
234                     'DNS_UPSTREAM', 'NTP1', 'NTP2', 'NTP3', 'FUEL_ACCESS']:
235             fuel[key] = data[key]
236         for key in fuel['ADMIN_NETWORK'].keys():
237             if key not in ['ipaddress', 'netmask',
238                            'dhcp_pool_start', 'dhcp_pool_end']:
239                 del fuel['ADMIN_NETWORK'][key]
240         self.write_yaml(self.dea_file, {'fuel': fuel})
241
242     def reap_network_settings(self):
243         network_file = ('%s/network_%s.yaml'
244                         % (self.temp_dir, self.env_id))
245         data = self.read_yaml(network_file)
246         network = {}
247         network['networking_parameters'] = data['networking_parameters']
248         network['networks'] = data['networks']
249         for net in network['networks']:
250             del net['id']
251             del net['group_id']
252         self.write_yaml(self.dea_file, {'network': network})
253
254     def reap_settings(self):
255         settings_file = '%s/settings_%s.yaml' % (self.temp_dir, self.env_id)
256         settings = self.read_yaml(settings_file)
257         self.write_yaml(self.dea_file, {'settings': settings})
258
259     def get_interface(self, real_node_id):
260         exec_cmd('fuel node --node-id %s --network --download --dir %s'
261                  % (real_node_id, self.temp_dir))
262         interface_file = ('%s/node_%s/interfaces.yaml'
263                           % (self.temp_dir, real_node_id))
264         interfaces = self.read_yaml(interface_file)
265         interface_config = {}
266         pxe_mac = None
267         for interface in interfaces:
268             networks = []
269             for network in interface['assigned_networks']:
270                 networks.append(network['name'])
271                 if network['name'] == 'fuelweb_admin':
272                     pxe_mac = interface['mac']
273             if networks:
274                 interface_config[interface['name']] = networks
275         return interface_config, pxe_mac
276
277     def read_yaml(self, yaml_file):
278         with open(yaml_file) as f:
279             data = yaml.load(f)
280             return data
281
282     def intro(self):
283         delete(self.dea_file)
284         delete(self.dha_file)
285         self.temp_dir = exec_cmd('mktemp -d')
286         date = time.strftime('%c')
287         self.write(self.dea_file,
288                    DEA_1.format(date=date, comment=self.comment), False)
289         self.write(self.dha_file,
290                    DHA_1.format(date=date, comment=self.comment))
291         self.get_env()
292         self.download_config('deployment')
293         self.download_config('settings')
294         self.download_config('network')
295
296     def finale(self):
297         log('DEA file is available at %s' % self.dea_file)
298         log('DHA file is available at %s (this is just a template)'
299             % self.dha_file)
300         shutil.rmtree(self.temp_dir)
301
302     def reap(self):
303         self.intro()
304         self.reap_environment_info()
305         self.reap_nodes_interfaces_transformations()
306         self.reap_fuel_settings()
307         self.reap_network_settings()
308         self.reap_settings()
309         self.finale()
310
311
312 def usage():
313     print '''
314     Usage:
315     python reap.py <dea_file> <dha_file> <comment>
316     '''
317
318
319 def parse_arguments():
320     parser = ArgParser(prog='python %s' % __file__)
321     parser.add_argument('dea_file', nargs='?', action='store',
322                         default='dea.yaml',
323                         help='Deployment Environment Adapter: dea.yaml')
324     parser.add_argument('dha_file', nargs='?', action='store',
325                         default='dha.yaml',
326                         help='Deployment Hardware Adapter: dha.yaml')
327     parser.add_argument('comment', nargs='?', action='store', help='Comment')
328     args = parser.parse_args()
329     return (args.dea_file, args.dha_file, args.comment)
330
331
332 def main():
333     dea_file, dha_file, comment = parse_arguments()
334
335     r = Reap(dea_file, dha_file, comment)
336     r.reap()
337
338
339 if __name__ == '__main__':
340     main()