blob: 8be8c908e4655f196fdb76c25511fc4e313b52d6 [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ćc4b33092017-02-15 13:25:38 +0100392class BootResource(MaasObject):
393 def __init__(self):
394 super(BootResource, self).__init__()
395 self._all_elements_url = u'api/2.0/boot-resources/'
396 self._create_url = u'api/2.0/boot-resources/'
397 self._update_url = u'api/2.0/boot-resources/{0}/'
398 self._config_path = 'region.boot_resources'
399
400 def fill_data(self, name, boot_data):
401 sha256 = hashlib.sha256()
402 sha256.update(file(boot_data['content']).read())
403 data = {
404 'name': name,
405 'title': boot_data['title'],
406 'architecture': boot_data['architecture'],
407 'filetype': boot_data['filetype'],
408 'size': str(os.path.getsize(boot_data['content'])),
409 'sha256': sha256.hexdigest(),
410 'content': io.open(boot_data['content']),
411 }
412 return data
413
414 def update(self, new, old):
415 self._update = False
416 return new
417
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100418class CommissioningScripts(MaasObject):
419 def __init__(self):
420 super(CommissioningScripts, self).__init__()
421 self._all_elements_url = u'api/2.0/commissioning-scripts/'
422 self._create_url = u'api/2.0/commissioning-scripts/'
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100423 self._config_path = 'region.commissioning_scripts'
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100424 self._update_url = u'api/2.0/commissioning-scripts/{0}'
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100425 self._update_key = 'name'
426
427 def fill_data(self, name, file_path):
428 data = {
429 'name': name,
430 'content': io.open(file_path),
431 }
432 return data
433
434 def update(self, new, old):
435 return new
436
437class MaasConfig(MaasObject):
438 def __init__(self):
439 super(MaasConfig, self).__init__()
440 self._all_elements_url = None
441 self._create_url = (u'api/2.0/maas/', u'set_config')
442 self._config_path = 'region.maas_config'
443
444 def fill_data(self, name, value):
445 data = {
446 'name': name,
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100447 'value': str(value),
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100448 }
449 return data
450
451 def update(self, new, old):
452 self._update = False
453 return new
454
455
Krzysztof Szukiełojća1bd77e2017-03-30 08:34:22 +0200456class SSHPrefs(MaasObject):
457 def __init__(self):
458 super(SSHPrefs, self).__init__()
459 self._all_elements_url = None
460 self._create_url = u'api/2.0/account/prefs/sshkeys/'
461 self._config_path = 'region.sshprefs'
462 self._element_key = 'hostname'
463 self._update_key = 'system_id'
464
465 def fill_data(self, value):
466 data = {
467 'key': value,
468 }
469 return data
470
471 def process(self):
472 config = __salt__['config.get']('maas')
473 for part in self._config_path.split('.'):
474 config = config.get(part, {})
475 extra = {}
476 for name, url_call in self._extra_data_urls.iteritems():
477 key = 'id'
478 if isinstance(url_call, tuple):
479 url_call, key = url_call[:]
480 extra[name] = {v['name']: v[key] for v in
481 json.loads(self._maas.get(url_call).read())}
482 if self._all_elements_url:
483 all_elements = {}
484 elements = self._maas.get(self._all_elements_url).read()
485 res_json = json.loads(elements)
486 for element in res_json:
487 if isinstance(element, (str, unicode)):
488 all_elements[element] = {}
489 else:
490 all_elements[element[self._element_key]] = element
491 else:
492 all_elements = {}
493 ret = {
494 'success': [],
495 'errors': {},
496 'updated': [],
497 }
498 for config_data in config:
499 name = config_data[:10]
500 try:
501 data = self.fill_data(config_data, **extra)
502 self.send(data)
503 ret['success'].append(name)
504 except urllib2.HTTPError as e:
505 etxt = e.read()
506 LOG.exception('Failed for object %s reason %s', name, etxt)
507 ret['errors'][name] = str(etxt)
508 except Exception as e:
509 LOG.exception('Failed for object %s reason %s', name, e)
510 ret['errors'][name] = str(e)
511 if ret['errors']:
512 raise Exception(ret)
513 return ret
Krzysztof Szukiełojć8cc32b42017-03-29 15:22:57 +0200514
515class Domain(MaasObject):
516 def __init__(self):
517 super(Domain, self).__init__()
518 self._all_elements_url = u'/api/2.0/domains/'
519 self._create_url = u'/api/2.0/domains/'
520 self._config_path = 'region.domain'
521 self._update_url = u'/api/2.0/domains/{0}/'
522
523 def fill_data(self, value):
524 data = {
525 'name': value,
526 }
527 self._update = True
528 return data
529
530 def update(self, new, old):
531 new['id'] = str(old['id'])
532 new['authoritative'] = str(old['authoritative'])
533 return new
534
535 def process(self):
536 config = __salt__['config.get']('maas')
537 for part in self._config_path.split('.'):
538 config = config.get(part, {})
539 extra = {}
540 for name, url_call in self._extra_data_urls.iteritems():
541 key = 'id'
542 if isinstance(url_call, tuple):
543 url_call, key = url_call[:]
544 extra[name] = {v['name']: v[key] for v in
545 json.loads(self._maas.get(url_call).read())}
546 if self._all_elements_url:
547 all_elements = {}
548 elements = self._maas.get(self._all_elements_url).read()
549 res_json = json.loads(elements)
550 for element in res_json:
551 if isinstance(element, (str, unicode)):
552 all_elements[element] = {}
553 else:
554 all_elements[element[self._element_key]] = element
555 else:
556 all_elements = {}
557 ret = {
558 'success': [],
559 'errors': {},
560 'updated': [],
561 }
562 try:
563 data = self.fill_data(config, **extra)
564 data = self.update(data, all_elements.values()[0])
565 self.send(data)
566 ret['success'].append('domain')
567 except urllib2.HTTPError as e:
568 etxt = e.read()
569 LOG.exception('Failed for object %s reason %s', 'domain', etxt)
570 ret['errors']['domain'] = str(etxt)
571 except Exception as e:
572 LOG.exception('Failed for object %s reason %s', 'domain', e)
573 ret['errors']['domain'] = str(e)
574 if ret['errors']:
575 raise Exception(ret)
576 return ret
577
578
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200579class MachinesStatus(MaasObject):
580 @classmethod
581 def execute(cls):
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200582 cls._maas = _create_maas_client()
583 result = cls._maas.get(u'api/2.0/machines/')
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200584 json_result = json.loads(result.read())
585 res = []
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200586 status_name_dict = dict([
587 (0, 'New'), (1, 'Commissioning'), (2, 'Failed commissioning'),
588 (3, 'Missing'), (4, 'Ready'), (5, 'Reserved'), (10, 'Allocated'),
589 (9, 'Deploying'), (6, 'Deployed'), (7, 'Retired'), (8, 'Broken'),
590 (11, 'Failed deployment'), (12, 'Releasing'),
591 (13, 'Releasing failed'), (14, 'Disk erasing'),
592 (15, 'Failed disk erasing')])
593 summary = collections.Counter()
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200594 for machine in json_result:
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200595 status = status_name_dict[machine['status']]
596 summary[status] += 1
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200597 res.append({
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200598 'hostname': machine['hostname'],
599 'system_id': machine['system_id'],
600 'status': status,
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200601 })
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200602 return {'machines':res, 'summary': summary}
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200603
604
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100605def process_fabrics():
606 return Fabric().process()
607
608def process_subnets():
609 return Subnet().process()
610
611def process_dhcp_snippets():
612 return DHCPSnippet().process()
613
614def process_package_repositories():
615 return PacketRepository().process()
616
617def process_devices():
618 return Device().process()
619
620def process_machines():
621 return Machine().process()
622
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200623def process_assign_machines_ip():
624 return AssignMachinesIP().process()
625
626def machines_status():
627 return MachinesStatus.execute()
628
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100629def process_boot_resources():
630 return BootResource().process()
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100631
632def process_maas_config():
633 return MaasConfig().process()
634
635def process_commissioning_scripts():
636 return CommissioningScripts().process()
Krzysztof Szukiełojć8cc32b42017-03-29 15:22:57 +0200637
638def process_domain():
639 return Domain().process()
Krzysztof Szukiełojća1bd77e2017-03-30 08:34:22 +0200640
641def process_sshprefs():
642 return SSHPrefs().process()