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