blob: 87aa56839783cdcd3991cc1c0236672ec72e95d1 [file] [log] [blame]
Ales Komarek663b85c2016-03-11 14:26:42 +01001# -*- coding: utf-8 -*-
2'''
3Module for handling maas calls.
4
5:optdepends: pyapi-maas Python adapter
6:configuration: This module is not usable until the following are specified
7 either in a pillar or in the minion's config file::
8
9 maas.url: 'https://maas.domain.com/'
10 maas.token: fdsfdsdsdsfa:fsdfae3fassd:fdsfdsfsafasdfsa
11
12'''
13
14from __future__ import absolute_import
15
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +010016import io
Ales Komarek663b85c2016-03-11 14:26:42 +010017import logging
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +010018import os.path
19import subprocess
20import urllib2
21import hashlib
Ales Komarek663b85c2016-03-11 14:26:42 +010022
smolaon27359ae2016-03-11 17:15:34 +010023import json
24
Ales Komarek663b85c2016-03-11 14:26:42 +010025LOG = logging.getLogger(__name__)
26
27# Import third party libs
28HAS_MASS = False
29try:
Damian Szelugad0ac0ac2017-03-29 15:15:33 +020030 from maas_client import MAASClient, MAASDispatcher, MAASOAuth
Ales Komarek663b85c2016-03-11 14:26:42 +010031 HAS_MASS = True
32except ImportError:
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +010033 LOG.exception('why??')
Ales Komarek663b85c2016-03-11 14:26:42 +010034
35def __virtual__():
36 '''
37 Only load this module if maas-client
38 is installed on this minion.
39 '''
40 if HAS_MASS:
41 return 'maas'
42 return False
43
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +010044APIKEY_FILE = '/var/lib/maas/.maas_credentials'
45
46def _format_data(data):
47 class Lazy:
48 def __str__(self):
49 return ' '.join(['{0}={1}'.format(k, v)
50 for k, v in data.iteritems()])
51
52 return Lazy()
Ales Komarek663b85c2016-03-11 14:26:42 +010053
54
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +010055def _create_maas_client():
56 global APIKEY_FILE
57 try:
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +010058 api_token = file(APIKEY_FILE).read().splitlines()[-1].strip().split(':')
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +010059 except:
60 LOG.exception('token')
61 auth = MAASOAuth(*api_token)
62 api_url = 'http://localhost:5240/MAAS'
Ales Komarek663b85c2016-03-11 14:26:42 +010063 dispatcher = MAASDispatcher()
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +010064 return MAASClient(auth, dispatcher, api_url)
Ales Komarek663b85c2016-03-11 14:26:42 +010065
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +010066class MaasObject(object):
67 def __init__(self):
68 self._maas = _create_maas_client()
69 self._extra_data_urls = {}
70 self._extra_data = {}
71 self._update = False
72 self._element_key = 'name'
73 self._update_key = 'id'
74
75 def send(self, data):
76 LOG.info('%s %s', self.__class__.__name__.lower(), _format_data(data))
77 if self._update:
78 return self._maas.put(self._update_url.format(data[self._update_key]), **data).read()
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +010079 if isinstance(self._create_url, tuple):
Krzysztof Szukiełojćd57a32d2017-04-04 11:25:02 +020080 return self._maas.post(self._create_url[0].format(**data),
81 *self._create_url[1:], **data).read()
82 return self._maas.post(self._create_url.format(**data), None, **data).read()
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +010083
84 def process(self):
Krzysztof Szukiełojćd57a32d2017-04-04 11:25:02 +020085 try:
86 config = __salt__['config.get']('maas')
87 for part in self._config_path.split('.'):
88 config = config.get(part, {})
89 extra = {}
90 for name, url_call in self._extra_data_urls.iteritems():
91 key = 'id'
92 key_name = 'name'
93 if isinstance(url_call, tuple):
94 if len(url_call) == 2:
95 url_call, key = url_call[:]
96 else:
97 url_call, key, key_name = url_call[:]
98 if key:
99 extra[name] = {v[key_name]: v[key] for v in
100 json.loads(self._maas.get(url_call).read())}
101 else:
102 extra[name] = {v[key_name]: v for v in
103 json.loads(self._maas.get(url_call).read())}
104 if self._all_elements_url:
105 all_elements = {}
106 elements = self._maas.get(self._all_elements_url).read()
107 res_json = json.loads(elements)
108 for element in res_json:
109 if isinstance(element, (str, unicode)):
110 all_elements[element] = {}
111 else:
112 all_elements[element[self._element_key]] = element
113 else:
114 all_elements = {}
115 ret = {
116 'success': [],
117 'errors': {},
118 'updated': [],
119 }
120 for name, config_data in config.iteritems():
121 self._update = False
122 try:
123 data = self.fill_data(name, config_data, **extra)
124 if data is None:
125 ret['updated'].append(name)
126 continue
127 if name in all_elements:
128 self._update = True
129 data = self.update(data, all_elements[name])
130 self.send(data)
131 ret['updated'].append(name)
132 else:
133 self.send(data)
134 ret['success'].append(name)
135 except urllib2.HTTPError as e:
136 etxt = e.read()
137 LOG.exception('Failed for object %s reason %s', name, etxt)
138 ret['errors'][name] = str(etxt)
139 except Exception as e:
140 LOG.exception('Failed for object %s reason %s', name, e)
141 ret['errors'][name] = str(e)
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200142 except Exception as e:
143 LOG.exception('Error Global')
144 raise
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100145 if ret['errors']:
146 raise Exception(ret)
147 return ret
Ales Komarek663b85c2016-03-11 14:26:42 +0100148
149
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100150class Fabric(MaasObject):
151 def __init__(self):
152 super(Fabric, self).__init__()
153 self._all_elements_url = u'api/2.0/fabrics/'
154 self._create_url = u'api/2.0/fabrics/'
155 self._update_url = u'api/2.0/fabrics/{0}/'
156 self._config_path = 'region.fabrics'
Ales Komarek663b85c2016-03-11 14:26:42 +0100157
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100158 def fill_data(self, name, fabric):
159 data = {
160 'name': name,
161 'description': fabric.get('description', ''),
162 }
163 if 'class_type' in fabric:
164 data['class_type'] = fabric.get('class_type'),
165 return data
Ales Komarek663b85c2016-03-11 14:26:42 +0100166
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100167 def update(self, new, old):
168 new['id'] = str(old['id'])
169 return new
Ales Komarek663b85c2016-03-11 14:26:42 +0100170
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100171class Subnet(MaasObject):
172 def __init__(self):
173 super(Subnet, self).__init__()
174 self._all_elements_url = u'api/2.0/subnets/'
175 self._create_url = u'api/2.0/subnets/'
176 self._update_url = u'api/2.0/subnets/{0}/'
177 self._config_path = 'region.subnets'
178 self._extra_data_urls = {'fabrics':u'api/2.0/fabrics/'}
Ales Komarek0fafa572016-03-11 14:56:44 +0100179
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100180 def fill_data(self, name, subnet, fabrics):
181 data = {
182 'name': name,
183 'fabric': str(fabrics[subnet.get('fabric', '')]),
184 'cidr': subnet.get('cidr'),
185 'gateway_ip': subnet['gateway_ip'],
186 }
187 self._iprange = subnet['iprange']
188 return data
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100189
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100190 def update(self, new, old):
191 new['id'] = str(old['id'])
192 return new
Ales Komarek0fafa572016-03-11 14:56:44 +0100193
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100194 def send(self, data):
195 response = super(Subnet, self).send(data)
196 res_json = json.loads(response)
197 self._process_iprange(res_json['id'])
198 return response
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100199
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100200 def _process_iprange(self, subnet_id):
201 ipranges = json.loads(self._maas.get(u'api/2.0/ipranges/').read())
202 LOG.warn('all %s ipranges %s', subnet_id, ipranges)
203 update = False
204 old_data = None
205 for iprange in ipranges:
206 if iprange['subnet']['id'] == subnet_id:
207 update = True
208 old_data = iprange
209 break
210 data = {
211 'start_ip': self._iprange.get('start'),
212 'end_ip': self._iprange.get('end'),
213 'subnet': str(subnet_id),
214 'type': self._iprange.get('type', 'dynamic')
215 }
216 LOG.warn('INFO: %s\n OLD: %s', data, old_data)
217 LOG.info('iprange %s', _format_data(data))
218 if update:
219 LOG.warn('UPDATING %s %s', data, old_data)
220 self._maas.put(u'api/2.0/ipranges/{0}/'.format(old_data['id']), **data)
smolaonc3385f82016-03-11 19:01:24 +0100221 else:
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100222 self._maas.post(u'api/2.0/ipranges/', None, **data)
smolaonc3385f82016-03-11 19:01:24 +0100223
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100224class DHCPSnippet(MaasObject):
225 def __init__(self):
226 super(DHCPSnippet, self).__init__()
227 self._all_elements_url = u'api/2.0/dhcp-snippets/'
228 self._create_url = u'api/2.0/dhcp-snippets/'
229 self._update_url = u'api/2.0/dhcp-snippets/{0}/'
230 self._config_path = 'region.dhcp_snippets'
231 self._extra_data_urls = {'subnets': u'api/2.0/subnets/'}
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100232
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100233 def fill_data(self, name, snippet, subnets):
234 data = {
235 'name': name,
236 'value': snippet['value'],
237 'description': snippet['description'],
238 'enabled': str(snippet['enabled'] and 1 or 0),
239 'subnet': str(subnets[snippet['subnet']]),
240 }
241 return data
smolaonc3385f82016-03-11 19:01:24 +0100242
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100243 def update(self, new, old):
244 new['id'] = str(old['id'])
245 return new
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100246
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100247class PacketRepository(MaasObject):
248 def __init__(self):
249 super(PacketRepository, self).__init__()
250 self._all_elements_url = u'api/2.0/package-repositories/'
251 self._create_url = u'api/2.0/package-repositories/'
252 self._update_url = u'api/2.0/package-repositories/{0}/'
253 self._config_path = 'region.package_repositories'
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100254
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100255 def fill_data(self, name, package_repository):
256 data = {
257 'name': name,
258 'url': package_repository['url'],
259 'distributions': package_repository['distributions'],
260 'components': package_repository['components'],
261 'arches': package_repository['arches'],
262 'key': package_repository['key'],
263 'enabled': str(package_repository['enabled'] and 1 or 0),
264 }
265 if 'disabled_pockets' in package_repository:
266 data['disabled_pockets'] = package_repository['disable_pockets'],
267 return data
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100268
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100269 def update(self, new, old):
270 new['id'] = str(old['id'])
271 return new
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100272
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100273class Device(MaasObject):
274 def __init__(self):
275 super(Device, self).__init__()
276 self._all_elements_url = u'api/2.0/devices/'
277 self._create_url = u'api/2.0/devices/'
278 self._update_url = u'api/2.0/devices/{0}/'
279 self._config_path = 'region.devices'
280 self._element_key = 'hostname'
281 self._update_key = 'system_id'
smolaonc3385f82016-03-11 19:01:24 +0100282
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100283 def fill_data(self, name, device_data):
284 data = {
285 'mac_addresses': device_data['mac'],
286 'hostname': name,
287 }
288 self._interface = device_data['interface']
289 return data
290
291 def update(self, new, old):
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100292 old_macs = set(v['mac_address'].lower() for v in old['interface_set'])
293 if new['mac_addresses'].lower() not in old_macs:
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100294 self._update = False
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100295 LOG.info('Mac changed deleting old device %s', old['system_id'])
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100296 self._maas.delete(u'api/2.0/devices/{0}/'.format(old['system_id']))
297 else:
298 new[self._update_key] = str(old[self._update_key])
299 return new
300
301 def send(self, data):
302 response = super(Device, self).send(data)
303 resp_json = json.loads(response)
304 system_id = resp_json['system_id']
305 iface_id = resp_json['interface_set'][0]['id']
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100306 self._link_interface(system_id, iface_id)
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100307 return response
308
309 def _link_interface(self, system_id, interface_id):
310 data = {
311 'mode': self._interface.get('mode', 'STATIC'),
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100312 'subnet': self._interface['subnet'],
313 'ip_address': self._interface['ip_address'],
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100314 }
315 if 'default_gateway' in self._interface:
316 data['default_gateway'] = self._interface.get('default_gateway')
317 if self._update:
318 data['force'] = '1'
319 LOG.info('interfaces link_subnet %s %s %s', system_id, interface_id,
320 _format_data(data))
321 self._maas.post(u'/api/2.0/nodes/{0}/interfaces/{1}/'
322 .format(system_id, interface_id), 'link_subnet',
323 **data)
324
325
326class Machine(MaasObject):
327 def __init__(self):
328 super(Machine, self).__init__()
329 self._all_elements_url = u'api/2.0/machines/'
330 self._create_url = u'api/2.0/machines/'
331 self._update_url = u'api/2.0/machines/{0}/'
332 self._config_path = 'region.machines'
333 self._element_key = 'hostname'
334 self._update_key = 'system_id'
335
336 def fill_data(self, name, machine_data):
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100337 power_data = machine_data['power_parameters']
338 data = {
339 'hostname': name,
340 'architecture': machine_data.get('architecture', 'amd64/generic'),
Krzysztof Szukiełojćd57a32d2017-04-04 11:25:02 +0200341 'mac_addresses': machine_data['interface']['mac'],
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100342 'power_type': machine_data.get('power_type', 'ipmi'),
343 'power_parameters_power_address': power_data['power_address'],
344 }
345 if 'power_user' in power_data:
346 data['power_parameters_power_user'] = power_data['power_user']
347 if 'power_password' in power_data:
348 data['power_parameters_power_pass'] = \
349 power_data['power_password']
350 return data
351
352 def update(self, new, old):
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100353 old_macs = set(v['mac_address'].lower() for v in old['interface_set'])
354 if new['mac_addresses'].lower() not in old_macs:
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100355 self._update = False
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100356 LOG.info('Mac changed deleting old machine %s', old['system_id'])
357 self._maas.delete(u'api/2.0/machines/{0}/'.format(old['system_id']))
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100358 else:
359 new[self._update_key] = str(old[self._update_key])
360 return new
361
Krzysztof Szukiełojćd57a32d2017-04-04 11:25:02 +0200362
363class AssignMachinesIP(MaasObject):
364 def __init__(self):
365 super(AssignMachinesIP, self).__init__()
366 self._all_elements_url = None
367 self._create_url = (u'/api/2.0/nodes/{system_id}/interfaces/{interface_id}/', 'link_subnet')
368 self._config_path = 'region.machines'
369 self._element_key = 'hostname'
370 self._update_key = 'system_id'
371 self._extra_data_urls = {'machines' : (u'api/2.0/machines/', None, 'hostname')}
372
373 def fill_data(self, name, data, machines):
374 interface = data['interface']
375 machine = machines[name]
376 if 'ip' not in interface:
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100377 return
378 data = {
379 'mode': 'STATIC',
Krzysztof Szukiełojćd57a32d2017-04-04 11:25:02 +0200380 'subnet': str(interface.get('subnet')),
381 'ip_address': str(interface.get('ip')),
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100382 }
Krzysztof Szukiełojćd57a32d2017-04-04 11:25:02 +0200383 if 'default_gateway' in interface:
384 data['default_gateway'] = interface.get('gateway')
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100385 if self._update:
386 data['force'] = '1'
Krzysztof Szukiełojćd57a32d2017-04-04 11:25:02 +0200387 data['system_id'] = str(machine['system_id'])
388 data['interface_id'] = str(machine['interface_set'][0]['id'])
389 return data
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100390
391
Krzysztof Szukiełojć008d7d42017-04-05 15:26:01 +0200392
393
Krzysztof Szukiełojć7c16e052017-04-05 10:04:45 +0200394class DeployMachines(MaasObject):
Krzysztof Szukiełojć008d7d42017-04-05 15:26:01 +0200395 READY = 4
Krzysztof Szukiełojć7c16e052017-04-05 10:04:45 +0200396 def __init__(self):
397 super(DeployMachines, self).__init__()
398 self._all_elements_url = None
399 self._create_url = (u'api/2.0/machines/{system_id}/', 'deploy')
400 self._config_path = 'region.machines'
401 self._element_key = 'hostname'
Krzysztof Szukiełojć008d7d42017-04-05 15:26:01 +0200402 self._extra_data_urls = {'machines': (u'api/2.0/machines/', None, 'hostname')}
Krzysztof Szukiełojć7c16e052017-04-05 10:04:45 +0200403
404 def fill_data(self, name, machine_data, machines):
Krzysztof Szukiełojć008d7d42017-04-05 15:26:01 +0200405 machine = machines[name]
406 if machine['status'] != self.READY:
407 raise Exception('Not in ready state')
Krzysztof Szukiełojć7c16e052017-04-05 10:04:45 +0200408 data = {
Krzysztof Szukiełojć008d7d42017-04-05 15:26:01 +0200409 'system_id': machine['system_id'],
Krzysztof Szukiełojć7c16e052017-04-05 10:04:45 +0200410 }
411 if 'os' in machine_data:
Krzysztof Szukiełojć008d7d42017-04-05 15:26:01 +0200412 data['distro_series'] = machine_data['distro_series']
Krzysztof Szukiełojć7c16e052017-04-05 10:04:45 +0200413 if 'hwe_kernel' in machine_data:
Krzysztof Szukiełojć008d7d42017-04-05 15:26:01 +0200414 data['hwe_kernel'] = machine_data['hwe_kernel']
Krzysztof Szukiełojć7c16e052017-04-05 10:04:45 +0200415 return data
416
417
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100418class BootResource(MaasObject):
419 def __init__(self):
420 super(BootResource, self).__init__()
421 self._all_elements_url = u'api/2.0/boot-resources/'
422 self._create_url = u'api/2.0/boot-resources/'
423 self._update_url = u'api/2.0/boot-resources/{0}/'
424 self._config_path = 'region.boot_resources'
425
426 def fill_data(self, name, boot_data):
427 sha256 = hashlib.sha256()
428 sha256.update(file(boot_data['content']).read())
429 data = {
430 'name': name,
431 'title': boot_data['title'],
432 'architecture': boot_data['architecture'],
433 'filetype': boot_data['filetype'],
434 'size': str(os.path.getsize(boot_data['content'])),
435 'sha256': sha256.hexdigest(),
436 'content': io.open(boot_data['content']),
437 }
438 return data
439
440 def update(self, new, old):
441 self._update = False
442 return new
443
Krzysztof Szukiełojć7c16e052017-04-05 10:04:45 +0200444
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100445class CommissioningScripts(MaasObject):
446 def __init__(self):
447 super(CommissioningScripts, self).__init__()
448 self._all_elements_url = u'api/2.0/commissioning-scripts/'
449 self._create_url = u'api/2.0/commissioning-scripts/'
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100450 self._config_path = 'region.commissioning_scripts'
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100451 self._update_url = u'api/2.0/commissioning-scripts/{0}'
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100452 self._update_key = 'name'
453
454 def fill_data(self, name, file_path):
455 data = {
456 'name': name,
457 'content': io.open(file_path),
458 }
459 return data
460
461 def update(self, new, old):
462 return new
463
464class MaasConfig(MaasObject):
465 def __init__(self):
466 super(MaasConfig, self).__init__()
467 self._all_elements_url = None
468 self._create_url = (u'api/2.0/maas/', u'set_config')
469 self._config_path = 'region.maas_config'
470
471 def fill_data(self, name, value):
472 data = {
473 'name': name,
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100474 'value': str(value),
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100475 }
476 return data
477
478 def update(self, new, old):
479 self._update = False
480 return new
481
482
Krzysztof Szukiełojća1bd77e2017-03-30 08:34:22 +0200483class SSHPrefs(MaasObject):
484 def __init__(self):
485 super(SSHPrefs, self).__init__()
486 self._all_elements_url = None
487 self._create_url = u'api/2.0/account/prefs/sshkeys/'
488 self._config_path = 'region.sshprefs'
489 self._element_key = 'hostname'
490 self._update_key = 'system_id'
491
492 def fill_data(self, value):
493 data = {
494 'key': value,
495 }
496 return data
497
498 def process(self):
499 config = __salt__['config.get']('maas')
500 for part in self._config_path.split('.'):
501 config = config.get(part, {})
502 extra = {}
503 for name, url_call in self._extra_data_urls.iteritems():
504 key = 'id'
505 if isinstance(url_call, tuple):
506 url_call, key = url_call[:]
507 extra[name] = {v['name']: v[key] for v in
508 json.loads(self._maas.get(url_call).read())}
509 if self._all_elements_url:
510 all_elements = {}
511 elements = self._maas.get(self._all_elements_url).read()
512 res_json = json.loads(elements)
513 for element in res_json:
514 if isinstance(element, (str, unicode)):
515 all_elements[element] = {}
516 else:
517 all_elements[element[self._element_key]] = element
518 else:
519 all_elements = {}
520 ret = {
521 'success': [],
522 'errors': {},
523 'updated': [],
524 }
525 for config_data in config:
526 name = config_data[:10]
527 try:
528 data = self.fill_data(config_data, **extra)
529 self.send(data)
530 ret['success'].append(name)
531 except urllib2.HTTPError as e:
532 etxt = e.read()
533 LOG.exception('Failed for object %s reason %s', name, etxt)
534 ret['errors'][name] = str(etxt)
535 except Exception as e:
536 LOG.exception('Failed for object %s reason %s', name, e)
537 ret['errors'][name] = str(e)
538 if ret['errors']:
539 raise Exception(ret)
540 return ret
Krzysztof Szukiełojć8cc32b42017-03-29 15:22:57 +0200541
542class Domain(MaasObject):
543 def __init__(self):
544 super(Domain, self).__init__()
545 self._all_elements_url = u'/api/2.0/domains/'
546 self._create_url = u'/api/2.0/domains/'
547 self._config_path = 'region.domain'
548 self._update_url = u'/api/2.0/domains/{0}/'
549
550 def fill_data(self, value):
551 data = {
552 'name': value,
553 }
554 self._update = True
555 return data
556
557 def update(self, new, old):
558 new['id'] = str(old['id'])
559 new['authoritative'] = str(old['authoritative'])
560 return new
561
562 def process(self):
563 config = __salt__['config.get']('maas')
564 for part in self._config_path.split('.'):
565 config = config.get(part, {})
566 extra = {}
567 for name, url_call in self._extra_data_urls.iteritems():
568 key = 'id'
569 if isinstance(url_call, tuple):
570 url_call, key = url_call[:]
571 extra[name] = {v['name']: v[key] for v in
572 json.loads(self._maas.get(url_call).read())}
573 if self._all_elements_url:
574 all_elements = {}
575 elements = self._maas.get(self._all_elements_url).read()
576 res_json = json.loads(elements)
577 for element in res_json:
578 if isinstance(element, (str, unicode)):
579 all_elements[element] = {}
580 else:
581 all_elements[element[self._element_key]] = element
582 else:
583 all_elements = {}
584 ret = {
585 'success': [],
586 'errors': {},
587 'updated': [],
588 }
589 try:
590 data = self.fill_data(config, **extra)
591 data = self.update(data, all_elements.values()[0])
592 self.send(data)
593 ret['success'].append('domain')
594 except urllib2.HTTPError as e:
595 etxt = e.read()
596 LOG.exception('Failed for object %s reason %s', 'domain', etxt)
597 ret['errors']['domain'] = str(etxt)
598 except Exception as e:
599 LOG.exception('Failed for object %s reason %s', 'domain', e)
600 ret['errors']['domain'] = str(e)
601 if ret['errors']:
602 raise Exception(ret)
603 return ret
604
605
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200606class MachinesStatus(MaasObject):
607 @classmethod
608 def execute(cls):
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200609 cls._maas = _create_maas_client()
610 result = cls._maas.get(u'api/2.0/machines/')
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200611 json_result = json.loads(result.read())
612 res = []
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200613 status_name_dict = dict([
614 (0, 'New'), (1, 'Commissioning'), (2, 'Failed commissioning'),
615 (3, 'Missing'), (4, 'Ready'), (5, 'Reserved'), (10, 'Allocated'),
616 (9, 'Deploying'), (6, 'Deployed'), (7, 'Retired'), (8, 'Broken'),
617 (11, 'Failed deployment'), (12, 'Releasing'),
618 (13, 'Releasing failed'), (14, 'Disk erasing'),
619 (15, 'Failed disk erasing')])
620 summary = collections.Counter()
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200621 for machine in json_result:
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200622 status = status_name_dict[machine['status']]
623 summary[status] += 1
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200624 res.append({
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200625 'hostname': machine['hostname'],
626 'system_id': machine['system_id'],
627 'status': status,
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200628 })
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200629 return {'machines':res, 'summary': summary}
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200630
631
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100632def process_fabrics():
633 return Fabric().process()
634
635def process_subnets():
636 return Subnet().process()
637
638def process_dhcp_snippets():
639 return DHCPSnippet().process()
640
641def process_package_repositories():
642 return PacketRepository().process()
643
644def process_devices():
645 return Device().process()
646
647def process_machines():
648 return Machine().process()
649
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200650def process_assign_machines_ip():
651 return AssignMachinesIP().process()
652
653def machines_status():
654 return MachinesStatus.execute()
655
Krzysztof Szukiełojć7c16e052017-04-05 10:04:45 +0200656def deploy_machines():
657 return DeployMachines().process()
658
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100659def process_boot_resources():
660 return BootResource().process()
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100661
662def process_maas_config():
663 return MaasConfig().process()
664
665def process_commissioning_scripts():
666 return CommissioningScripts().process()
Krzysztof Szukiełojć8cc32b42017-03-29 15:22:57 +0200667
668def process_domain():
669 return Domain().process()
Krzysztof Szukiełojća1bd77e2017-03-30 08:34:22 +0200670
671def process_sshprefs():
672 return SSHPrefs().process()