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