2 ###############################################################################
3 # Copyright (c) 2015 Ericsson AB and others.
4 # szilard.cserey@ericsson.com
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9 ###############################################################################
32 title: Deployment Environment Adapter (DEA)
33 # DEA API version supported
40 title: Deployment Hardware Adapter (DHA)
41 # DHA API version supported
46 # Adapter to use for this definition
47 # adapter: [ipmi|libvirt]
51 # Mandatory properties are id and role.
52 # All other properties are adapter specific.
53 # For Non-Fuel nodes controlled by:
54 # - ipmi adapter you need to provide:
59 # - libvirt adapter you need to provide:
60 # libvirtName: <whatever>
61 # libvirtTemplate: [libvirt/vms/controller.xml | libvirt/vms/compute.xml]
63 # For the Fuel Node you need to provide:
64 # libvirtName: <whatever>
65 # libvirtTemplate: libvirt/vms/fuel.xml
72 # Adding the Fuel node as node id {node_id}
73 # which may not be correct - please adjust as needed.
76 DISKS = {'fuel': '50G',
83 def __init__(self, dea_file, dha_file, comment):
84 self.dea_file = dea_file
85 self.dha_file = dha_file
86 self.comment = comment
93 env_list = parse(exec_cmd('fuel env'))
95 err('Not exactly one environment')
96 self.env = env_list[0]
97 self.env_id = self.env[E['id']]
99 def download_config(self, config_type):
100 log('Download %s config for environment %s'
101 % (config_type, self.env_id))
102 exec_cmd('fuel %s --env %s --download --dir %s'
103 % (config_type, self.env_id, self.temp_dir))
105 def download_node_config(self, nodeid):
106 log('Download node %s config for environment %s to %s'
107 % (nodeid, self.env_id,self.temp_dir))
108 exec_cmd('fuel deployment --node-id %s --env %s --default --dir %s'
109 % (nodeid, self.env_id, self.temp_dir))
111 def write(self, file, text, newline=True):
112 mode = 'a' if os.path.isfile(file) else 'w'
113 with open(file, mode) as f:
114 f.write('%s%s' % (text, ('\n' if newline else '')))
116 def write_yaml(self, file, data, newline=True):
117 self.write(file, yaml.dump(data, default_flow_style=False).strip(),
120 def get_node_by_id(self, node_list, node_id):
121 for node in node_list:
122 if node[N['id']] == node_id:
125 def reap_interface(self, node_id, interfaces):
126 interface, mac = self.get_interface(node_id)
129 if_name = self.check_dict_exists(interfaces, interface)
131 if_name = 'interfaces_%s' % str(len(interfaces) + 1)
132 interfaces[if_name] = interface
135 def reap_transformation(self, node_id, roles, transformations):
136 main_role = 'controller' if 'controller' in roles else 'compute'
137 node_file = glob.glob('%s/deployment_%s/*%s_%s.yaml'
138 % (self.temp_dir, self.env_id,
141 with open(node_file[0]) as f:
142 node_config = yaml.load(f)
143 transformation = {'transformations':
144 node_config['network_scheme']['transformations']}
146 tr_name = self.check_dict_exists(transformations, transformation)
148 tr_name = 'transformations_%s' % str(len(transformations) + 1)
149 transformations[tr_name] = transformation
152 def check_dict_exists(self, main_dict, dict):
153 for key, val in main_dict.iteritems():
154 if cmp(dict, val) == 0:
157 def reap_nodes_interfaces_transformations(self):
158 node_list = parse(exec_cmd('fuel node'))
159 real_node_ids = [node[N['id']] for node in node_list]
161 min_node = real_node_ids[0]
167 for real_node_id in real_node_ids:
168 node_id = int(real_node_id) - int(min_node) + 1
169 self.last_node = node_id
170 node = self.get_node_by_id(node_list, real_node_id)
171 roles = commafy(node[N['roles']])
173 err('Fuel Node %s has no role' % real_node_id)
174 dea_node = {'id': node_id,
176 dha_node = {'id': node_id}
177 if_name, mac = self.reap_interface(real_node_id, interfaces)
178 log('reap transformation for node %s' % real_node_id)
179 tr_name = self.reap_transformation(real_node_id, roles,
182 {'interfaces': if_name,
183 'transformations': tr_name})
186 {'pxeMac': mac if mac else None,
191 'libvirtTemplate': None})
193 dea_nodes.append(dea_node)
194 dha_nodes.append(dha_node)
196 self.write_yaml(self.dha_file, {'nodes': dha_nodes}, False)
197 self.write_yaml(self.dea_file, {'nodes': dea_nodes})
198 self.write_yaml(self.dea_file, interfaces)
199 self.write_yaml(self.dea_file, transformations)
200 self.reap_fuel_node_info()
201 self.write_yaml(self.dha_file, {'disks': DISKS})
203 def reap_fuel_node_info(self):
206 'id': self.last_node + 1,
208 'libvirtTemplate': None,
211 'password': 'r00tme'}
213 dha_nodes.append(dha_node)
215 self.write(self.dha_file, DHA_2.format(node_id=dha_node['id']), False)
216 self.write_yaml(self.dha_file, dha_nodes)
218 def reap_environment_info(self):
219 network_file = ('%s/network_%s.yaml'
220 % (self.temp_dir, self.env_id))
221 network = self.read_yaml(network_file)
223 # ha_compact not understood by Fuel when deploying...OD
224 if self.env[E['mode']] == 'ha_compact':
225 self.env[E['mode']] = 'ha'
227 env = {'environment':
228 {'name': self.env[E['name']],
229 'mode': self.env[E['mode']],
231 network['networking_parameters']['segmentation_type']}}
232 self.write_yaml(self.dea_file, env)
233 wanted_release = None
234 rel_list = parse(exec_cmd('fuel release'))
236 if rel[R['id']] == self.env[E['release_id']]:
237 wanted_release = rel[R['name']]
238 self.write_yaml(self.dea_file, {'wanted_release': wanted_release})
240 def reap_fuel_settings(self):
241 data = self.read_yaml('/etc/fuel/astute.yaml')
243 del data['ADMIN_NETWORK']['mac']
244 del data['ADMIN_NETWORK']['interface']
245 for key in ['ADMIN_NETWORK', 'HOSTNAME', 'DNS_DOMAIN', 'DNS_SEARCH',
246 'DNS_UPSTREAM', 'NTP1', 'NTP2', 'NTP3', 'FUEL_ACCESS']:
247 fuel[key] = data[key]
248 for key in fuel['ADMIN_NETWORK'].keys():
249 if key not in ['ipaddress', 'netmask',
250 'dhcp_pool_start', 'dhcp_pool_end']:
251 del fuel['ADMIN_NETWORK'][key]
252 self.write_yaml(self.dea_file, {'fuel': fuel})
254 def reap_network_settings(self):
255 network_file = ('%s/network_%s.yaml'
256 % (self.temp_dir, self.env_id))
257 data = self.read_yaml(network_file)
259 network['networking_parameters'] = data['networking_parameters']
260 network['networks'] = data['networks']
261 for net in network['networks']:
264 self.write_yaml(self.dea_file, {'network': network})
266 def reap_settings(self):
267 settings_file = '%s/settings_%s.yaml' % (self.temp_dir, self.env_id)
268 settings = self.read_yaml(settings_file)
269 self.write_yaml(self.dea_file, {'settings': settings})
271 def get_interface(self, real_node_id):
272 exec_cmd('fuel node --node-id %s --network --download --dir %s'
273 % (real_node_id, self.temp_dir))
274 interface_file = ('%s/node_%s/interfaces.yaml'
275 % (self.temp_dir, real_node_id))
276 interfaces = self.read_yaml(interface_file)
277 interface_config = {}
279 for interface in interfaces:
281 for network in interface['assigned_networks']:
282 networks.append(network['name'])
283 if network['name'] == 'fuelweb_admin':
284 pxe_mac = interface['mac']
286 interface_config[interface['name']] = networks
287 return interface_config, pxe_mac
289 def read_yaml(self, yaml_file):
290 with open(yaml_file) as f:
295 delete(self.dea_file)
296 delete(self.dha_file)
297 self.temp_dir = exec_cmd('mktemp -d')
298 date = time.strftime('%c')
299 self.write(self.dea_file,
300 DEA_1.format(date=date, comment=self.comment), False)
301 self.write(self.dha_file,
302 DHA_1.format(date=date, comment=self.comment))
305 # Need to download deployment with explicit node ids
306 node_list = parse(exec_cmd('fuel node'))
307 real_node_ids = [node[N['id']] for node in node_list]
309 self.download_node_config(','.join(real_node_ids))
311 self.download_config('settings')
312 self.download_config('network')
315 log('DEA file is available at %s' % self.dea_file)
316 log('DHA file is available at %s (this is just a template)'
318 shutil.rmtree(self.temp_dir)
322 self.reap_environment_info()
323 self.reap_nodes_interfaces_transformations()
324 self.reap_fuel_settings()
325 self.reap_network_settings()
333 python reap.py <dea_file> <dha_file> <comment>
337 def parse_arguments():
338 parser = ArgParser(prog='python %s' % __file__)
339 parser.add_argument('dea_file', nargs='?', action='store',
341 help='Deployment Environment Adapter: dea.yaml')
342 parser.add_argument('dha_file', nargs='?', action='store',
344 help='Deployment Hardware Adapter: dha.yaml')
345 parser.add_argument('comment', nargs='?', action='store', help='Comment')
346 args = parser.parse_args()
347 return (args.dea_file, args.dha_file, args.comment)
351 dea_file, dha_file, comment = parse_arguments()
353 r = Reap(dea_file, dha_file, comment)
357 if __name__ == '__main__':