blob: 97e228058f299ba0926d296a4df2f15f695ecc53 [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ć7c16e052017-04-05 10:04:45 +0200392class DeployMachines(MaasObject):
393 def __init__(self):
394 super(DeployMachines, self).__init__()
395 self._all_elements_url = None
396 self._create_url = (u'api/2.0/machines/{system_id}/', 'deploy')
397 self._config_path = 'region.machines'
398 self._element_key = 'hostname'
399 self._extra_data_urls = {'machines': (u'api/2.0/machines/', 'system_id', 'hostname')}
400
401 def fill_data(self, name, machine_data, machines):
402 data = {
403 'system_id': machines[name],
404 }
405 if 'os' in machine_data:
406 data['distro_series'] = machine_data['os']
407 if 'hwe_kernel' in machine_data:
408 data['hwe_kernel'] = machine_data['kernel']
409 return data
410
411
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100412class BootResource(MaasObject):
413 def __init__(self):
414 super(BootResource, self).__init__()
415 self._all_elements_url = u'api/2.0/boot-resources/'
416 self._create_url = u'api/2.0/boot-resources/'
417 self._update_url = u'api/2.0/boot-resources/{0}/'
418 self._config_path = 'region.boot_resources'
419
420 def fill_data(self, name, boot_data):
421 sha256 = hashlib.sha256()
422 sha256.update(file(boot_data['content']).read())
423 data = {
424 'name': name,
425 'title': boot_data['title'],
426 'architecture': boot_data['architecture'],
427 'filetype': boot_data['filetype'],
428 'size': str(os.path.getsize(boot_data['content'])),
429 'sha256': sha256.hexdigest(),
430 'content': io.open(boot_data['content']),
431 }
432 return data
433
434 def update(self, new, old):
435 self._update = False
436 return new
437
Krzysztof Szukiełojć7c16e052017-04-05 10:04:45 +0200438
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100439class CommissioningScripts(MaasObject):
440 def __init__(self):
441 super(CommissioningScripts, self).__init__()
442 self._all_elements_url = u'api/2.0/commissioning-scripts/'
443 self._create_url = u'api/2.0/commissioning-scripts/'
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100444 self._config_path = 'region.commissioning_scripts'
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100445 self._update_url = u'api/2.0/commissioning-scripts/{0}'
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100446 self._update_key = 'name'
447
448 def fill_data(self, name, file_path):
449 data = {
450 'name': name,
451 'content': io.open(file_path),
452 }
453 return data
454
455 def update(self, new, old):
456 return new
457
458class MaasConfig(MaasObject):
459 def __init__(self):
460 super(MaasConfig, self).__init__()
461 self._all_elements_url = None
462 self._create_url = (u'api/2.0/maas/', u'set_config')
463 self._config_path = 'region.maas_config'
464
465 def fill_data(self, name, value):
466 data = {
467 'name': name,
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100468 'value': str(value),
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100469 }
470 return data
471
472 def update(self, new, old):
473 self._update = False
474 return new
475
476
Krzysztof Szukiełojća1bd77e2017-03-30 08:34:22 +0200477class SSHPrefs(MaasObject):
478 def __init__(self):
479 super(SSHPrefs, self).__init__()
480 self._all_elements_url = None
481 self._create_url = u'api/2.0/account/prefs/sshkeys/'
482 self._config_path = 'region.sshprefs'
483 self._element_key = 'hostname'
484 self._update_key = 'system_id'
485
486 def fill_data(self, value):
487 data = {
488 'key': value,
489 }
490 return data
491
492 def process(self):
493 config = __salt__['config.get']('maas')
494 for part in self._config_path.split('.'):
495 config = config.get(part, {})
496 extra = {}
497 for name, url_call in self._extra_data_urls.iteritems():
498 key = 'id'
499 if isinstance(url_call, tuple):
500 url_call, key = url_call[:]
501 extra[name] = {v['name']: v[key] for v in
502 json.loads(self._maas.get(url_call).read())}
503 if self._all_elements_url:
504 all_elements = {}
505 elements = self._maas.get(self._all_elements_url).read()
506 res_json = json.loads(elements)
507 for element in res_json:
508 if isinstance(element, (str, unicode)):
509 all_elements[element] = {}
510 else:
511 all_elements[element[self._element_key]] = element
512 else:
513 all_elements = {}
514 ret = {
515 'success': [],
516 'errors': {},
517 'updated': [],
518 }
519 for config_data in config:
520 name = config_data[:10]
521 try:
522 data = self.fill_data(config_data, **extra)
523 self.send(data)
524 ret['success'].append(name)
525 except urllib2.HTTPError as e:
526 etxt = e.read()
527 LOG.exception('Failed for object %s reason %s', name, etxt)
528 ret['errors'][name] = str(etxt)
529 except Exception as e:
530 LOG.exception('Failed for object %s reason %s', name, e)
531 ret['errors'][name] = str(e)
532 if ret['errors']:
533 raise Exception(ret)
534 return ret
Krzysztof Szukiełojć8cc32b42017-03-29 15:22:57 +0200535
536class Domain(MaasObject):
537 def __init__(self):
538 super(Domain, self).__init__()
539 self._all_elements_url = u'/api/2.0/domains/'
540 self._create_url = u'/api/2.0/domains/'
541 self._config_path = 'region.domain'
542 self._update_url = u'/api/2.0/domains/{0}/'
543
544 def fill_data(self, value):
545 data = {
546 'name': value,
547 }
548 self._update = True
549 return data
550
551 def update(self, new, old):
552 new['id'] = str(old['id'])
553 new['authoritative'] = str(old['authoritative'])
554 return new
555
556 def process(self):
557 config = __salt__['config.get']('maas')
558 for part in self._config_path.split('.'):
559 config = config.get(part, {})
560 extra = {}
561 for name, url_call in self._extra_data_urls.iteritems():
562 key = 'id'
563 if isinstance(url_call, tuple):
564 url_call, key = url_call[:]
565 extra[name] = {v['name']: v[key] for v in
566 json.loads(self._maas.get(url_call).read())}
567 if self._all_elements_url:
568 all_elements = {}
569 elements = self._maas.get(self._all_elements_url).read()
570 res_json = json.loads(elements)
571 for element in res_json:
572 if isinstance(element, (str, unicode)):
573 all_elements[element] = {}
574 else:
575 all_elements[element[self._element_key]] = element
576 else:
577 all_elements = {}
578 ret = {
579 'success': [],
580 'errors': {},
581 'updated': [],
582 }
583 try:
584 data = self.fill_data(config, **extra)
585 data = self.update(data, all_elements.values()[0])
586 self.send(data)
587 ret['success'].append('domain')
588 except urllib2.HTTPError as e:
589 etxt = e.read()
590 LOG.exception('Failed for object %s reason %s', 'domain', etxt)
591 ret['errors']['domain'] = str(etxt)
592 except Exception as e:
593 LOG.exception('Failed for object %s reason %s', 'domain', e)
594 ret['errors']['domain'] = str(e)
595 if ret['errors']:
596 raise Exception(ret)
597 return ret
598
599
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200600class MachinesStatus(MaasObject):
601 @classmethod
602 def execute(cls):
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200603 cls._maas = _create_maas_client()
604 result = cls._maas.get(u'api/2.0/machines/')
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200605 json_result = json.loads(result.read())
606 res = []
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200607 status_name_dict = dict([
608 (0, 'New'), (1, 'Commissioning'), (2, 'Failed commissioning'),
609 (3, 'Missing'), (4, 'Ready'), (5, 'Reserved'), (10, 'Allocated'),
610 (9, 'Deploying'), (6, 'Deployed'), (7, 'Retired'), (8, 'Broken'),
611 (11, 'Failed deployment'), (12, 'Releasing'),
612 (13, 'Releasing failed'), (14, 'Disk erasing'),
613 (15, 'Failed disk erasing')])
614 summary = collections.Counter()
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200615 for machine in json_result:
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200616 status = status_name_dict[machine['status']]
617 summary[status] += 1
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200618 res.append({
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200619 'hostname': machine['hostname'],
620 'system_id': machine['system_id'],
621 'status': status,
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200622 })
Krzysztof Szukiełojć0be1a162017-04-04 11:59:09 +0200623 return {'machines':res, 'summary': summary}
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200624
625
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100626def process_fabrics():
627 return Fabric().process()
628
629def process_subnets():
630 return Subnet().process()
631
632def process_dhcp_snippets():
633 return DHCPSnippet().process()
634
635def process_package_repositories():
636 return PacketRepository().process()
637
638def process_devices():
639 return Device().process()
640
641def process_machines():
642 return Machine().process()
643
Krzysztof Szukiełojć04e18332017-04-04 11:51:44 +0200644def process_assign_machines_ip():
645 return AssignMachinesIP().process()
646
647def machines_status():
648 return MachinesStatus.execute()
649
Krzysztof Szukiełojć7c16e052017-04-05 10:04:45 +0200650def deploy_machines():
651 return DeployMachines().process()
652
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100653def process_boot_resources():
654 return BootResource().process()
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100655
656def process_maas_config():
657 return MaasConfig().process()
658
659def process_commissioning_scripts():
660 return CommissioningScripts().process()
Krzysztof Szukiełojć8cc32b42017-03-29 15:22:57 +0200661
662def process_domain():
663 return Domain().process()
Krzysztof Szukiełojća1bd77e2017-03-30 08:34:22 +0200664
665def process_sshprefs():
666 return SSHPrefs().process()