blob: c2ba659b2a644af87246c6be2a8aee8bc521834e [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):
80 return self._maas.post(*self._create_url, **data).read()
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +010081 return self._maas.post(self._create_url, None, **data).read()
82
83 def process(self):
84 config = __salt__['config.get']('maas')
85 for part in self._config_path.split('.'):
86 config = config.get(part, {})
87 extra = {}
88 for name, url_call in self._extra_data_urls.iteritems():
89 key = 'id'
90 if isinstance(url_call, tuple):
91 url_call, key = url_call[:]
92 extra[name] = {v['name']: v[key] for v in
93 json.loads(self._maas.get(url_call).read())}
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +010094 if self._all_elements_url:
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +010095 all_elements = {}
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +010096 elements = self._maas.get(self._all_elements_url).read()
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +010097 res_json = json.loads(elements)
98 for element in res_json:
99 if isinstance(element, (str, unicode)):
100 all_elements[element] = {}
101 else:
102 all_elements[element[self._element_key]] = element
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100103 else:
104 all_elements = {}
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100105 ret = {
106 'success': [],
107 'errors': {},
108 'updated': [],
109 }
110 for name, config_data in config.iteritems():
111 try:
112 data = self.fill_data(name, config_data, **extra)
113 if name in all_elements:
114 self._update = True
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100115 data = self.update(data, all_elements[name])
116 self.send(data)
117 ret['updated'].append(name)
118 else:
119 self.send(data)
120 ret['success'].append(name)
121 except urllib2.HTTPError as e:
122 etxt = e.read()
123 LOG.exception('Failed for object %s reason %s', name, etxt)
124 ret['errors'][name] = str(etxt)
125 except Exception as e:
126 LOG.exception('Failed for object %s reason %s', name, e)
127 ret['errors'][name] = str(e)
128 if ret['errors']:
129 raise Exception(ret)
130 return ret
Ales Komarek663b85c2016-03-11 14:26:42 +0100131
132
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100133class Fabric(MaasObject):
134 def __init__(self):
135 super(Fabric, self).__init__()
136 self._all_elements_url = u'api/2.0/fabrics/'
137 self._create_url = u'api/2.0/fabrics/'
138 self._update_url = u'api/2.0/fabrics/{0}/'
139 self._config_path = 'region.fabrics'
Ales Komarek663b85c2016-03-11 14:26:42 +0100140
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100141 def fill_data(self, name, fabric):
142 data = {
143 'name': name,
144 'description': fabric.get('description', ''),
145 }
146 if 'class_type' in fabric:
147 data['class_type'] = fabric.get('class_type'),
148 return data
Ales Komarek663b85c2016-03-11 14:26:42 +0100149
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100150 def update(self, new, old):
151 new['id'] = str(old['id'])
152 return new
Ales Komarek663b85c2016-03-11 14:26:42 +0100153
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100154class Subnet(MaasObject):
155 def __init__(self):
156 super(Subnet, self).__init__()
157 self._all_elements_url = u'api/2.0/subnets/'
158 self._create_url = u'api/2.0/subnets/'
159 self._update_url = u'api/2.0/subnets/{0}/'
160 self._config_path = 'region.subnets'
161 self._extra_data_urls = {'fabrics':u'api/2.0/fabrics/'}
Ales Komarek0fafa572016-03-11 14:56:44 +0100162
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100163 def fill_data(self, name, subnet, fabrics):
164 data = {
165 'name': name,
166 'fabric': str(fabrics[subnet.get('fabric', '')]),
167 'cidr': subnet.get('cidr'),
168 'gateway_ip': subnet['gateway_ip'],
169 }
170 self._iprange = subnet['iprange']
171 return data
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100172
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100173 def update(self, new, old):
174 new['id'] = str(old['id'])
175 return new
Ales Komarek0fafa572016-03-11 14:56:44 +0100176
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100177 def send(self, data):
178 response = super(Subnet, self).send(data)
179 res_json = json.loads(response)
180 self._process_iprange(res_json['id'])
181 return response
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100182
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100183 def _process_iprange(self, subnet_id):
184 ipranges = json.loads(self._maas.get(u'api/2.0/ipranges/').read())
185 LOG.warn('all %s ipranges %s', subnet_id, ipranges)
186 update = False
187 old_data = None
188 for iprange in ipranges:
189 if iprange['subnet']['id'] == subnet_id:
190 update = True
191 old_data = iprange
192 break
193 data = {
194 'start_ip': self._iprange.get('start'),
195 'end_ip': self._iprange.get('end'),
196 'subnet': str(subnet_id),
197 'type': self._iprange.get('type', 'dynamic')
198 }
199 LOG.warn('INFO: %s\n OLD: %s', data, old_data)
200 LOG.info('iprange %s', _format_data(data))
201 if update:
202 LOG.warn('UPDATING %s %s', data, old_data)
203 self._maas.put(u'api/2.0/ipranges/{0}/'.format(old_data['id']), **data)
smolaonc3385f82016-03-11 19:01:24 +0100204 else:
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100205 self._maas.post(u'api/2.0/ipranges/', None, **data)
smolaonc3385f82016-03-11 19:01:24 +0100206
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100207class DHCPSnippet(MaasObject):
208 def __init__(self):
209 super(DHCPSnippet, self).__init__()
210 self._all_elements_url = u'api/2.0/dhcp-snippets/'
211 self._create_url = u'api/2.0/dhcp-snippets/'
212 self._update_url = u'api/2.0/dhcp-snippets/{0}/'
213 self._config_path = 'region.dhcp_snippets'
214 self._extra_data_urls = {'subnets': u'api/2.0/subnets/'}
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100215
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100216 def fill_data(self, name, snippet, subnets):
217 data = {
218 'name': name,
219 'value': snippet['value'],
220 'description': snippet['description'],
221 'enabled': str(snippet['enabled'] and 1 or 0),
222 'subnet': str(subnets[snippet['subnet']]),
223 }
224 return data
smolaonc3385f82016-03-11 19:01:24 +0100225
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100226 def update(self, new, old):
227 new['id'] = str(old['id'])
228 return new
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100229
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100230class PacketRepository(MaasObject):
231 def __init__(self):
232 super(PacketRepository, self).__init__()
233 self._all_elements_url = u'api/2.0/package-repositories/'
234 self._create_url = u'api/2.0/package-repositories/'
235 self._update_url = u'api/2.0/package-repositories/{0}/'
236 self._config_path = 'region.package_repositories'
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100237
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100238 def fill_data(self, name, package_repository):
239 data = {
240 'name': name,
241 'url': package_repository['url'],
242 'distributions': package_repository['distributions'],
243 'components': package_repository['components'],
244 'arches': package_repository['arches'],
245 'key': package_repository['key'],
246 'enabled': str(package_repository['enabled'] and 1 or 0),
247 }
248 if 'disabled_pockets' in package_repository:
249 data['disabled_pockets'] = package_repository['disable_pockets'],
250 return data
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100251
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100252 def update(self, new, old):
253 new['id'] = str(old['id'])
254 return new
Krzysztof Szukiełojć15b62b72017-02-15 08:58:18 +0100255
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100256class Device(MaasObject):
257 def __init__(self):
258 super(Device, self).__init__()
259 self._all_elements_url = u'api/2.0/devices/'
260 self._create_url = u'api/2.0/devices/'
261 self._update_url = u'api/2.0/devices/{0}/'
262 self._config_path = 'region.devices'
263 self._element_key = 'hostname'
264 self._update_key = 'system_id'
smolaonc3385f82016-03-11 19:01:24 +0100265
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100266 def fill_data(self, name, device_data):
267 data = {
268 'mac_addresses': device_data['mac'],
269 'hostname': name,
270 }
271 self._interface = device_data['interface']
272 return data
273
274 def update(self, new, old):
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100275 old_macs = set(v['mac_address'].lower() for v in old['interface_set'])
276 if new['mac_addresses'].lower() not in old_macs:
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100277 self._update = False
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100278 LOG.info('Mac changed deleting old device %s', old['system_id'])
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100279 self._maas.delete(u'api/2.0/devices/{0}/'.format(old['system_id']))
280 else:
281 new[self._update_key] = str(old[self._update_key])
282 return new
283
284 def send(self, data):
285 response = super(Device, self).send(data)
286 resp_json = json.loads(response)
287 system_id = resp_json['system_id']
288 iface_id = resp_json['interface_set'][0]['id']
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100289 self._link_interface(system_id, iface_id)
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100290 return response
291
292 def _link_interface(self, system_id, interface_id):
293 data = {
294 'mode': self._interface.get('mode', 'STATIC'),
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100295 'subnet': self._interface['subnet'],
296 'ip_address': self._interface['ip_address'],
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100297 }
298 if 'default_gateway' in self._interface:
299 data['default_gateway'] = self._interface.get('default_gateway')
300 if self._update:
301 data['force'] = '1'
302 LOG.info('interfaces link_subnet %s %s %s', system_id, interface_id,
303 _format_data(data))
304 self._maas.post(u'/api/2.0/nodes/{0}/interfaces/{1}/'
305 .format(system_id, interface_id), 'link_subnet',
306 **data)
307
308
309class Machine(MaasObject):
310 def __init__(self):
311 super(Machine, self).__init__()
312 self._all_elements_url = u'api/2.0/machines/'
313 self._create_url = u'api/2.0/machines/'
314 self._update_url = u'api/2.0/machines/{0}/'
315 self._config_path = 'region.machines'
316 self._element_key = 'hostname'
317 self._update_key = 'system_id'
318
319 def fill_data(self, name, machine_data):
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100320 self._interface = machine_data['interface']
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100321 power_data = machine_data['power_parameters']
322 data = {
323 'hostname': name,
324 'architecture': machine_data.get('architecture', 'amd64/generic'),
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100325 'mac_addresses': self._interface['mac'],
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100326 'power_type': machine_data.get('power_type', 'ipmi'),
327 'power_parameters_power_address': power_data['power_address'],
328 }
329 if 'power_user' in power_data:
330 data['power_parameters_power_user'] = power_data['power_user']
331 if 'power_password' in power_data:
332 data['power_parameters_power_pass'] = \
333 power_data['power_password']
334 return data
335
336 def update(self, new, old):
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100337 old_macs = set(v['mac_address'].lower() for v in old['interface_set'])
338 if new['mac_addresses'].lower() not in old_macs:
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100339 self._update = False
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100340 LOG.info('Mac changed deleting old machine %s', old['system_id'])
341 self._maas.delete(u'api/2.0/machines/{0}/'.format(old['system_id']))
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100342 else:
343 new[self._update_key] = str(old[self._update_key])
344 return new
345
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100346 def _link_interface(self, system_id, interface_id):
347 if 'ip' not in self._interface:
348 return
349 data = {
350 'mode': 'STATIC',
351 'subnet': self._interface.get('subnet'),
352 'ip_address': self._interface.get('ip'),
353 }
354 if 'default_gateway' in self._interface:
355 data['default_gateway'] = self._interface.get('gateway')
356 if self._update:
357 data['force'] = '1'
358 LOG.info('interfaces link_subnet %s %s %s', system_id, interface_id,
359 _format_data(data))
360 self._maas.post(u'/api/2.0/nodes/{0}/interfaces/{1}/'
361 .format(system_id, interface_id), 'link_subnet',
362 **data)
363
364 def send(self, data):
365 response = super(Machine, self).send(data)
366 resp_json = json.loads(response)
367 system_id = resp_json['system_id']
368 iface_id = resp_json['interface_set'][0]['id']
369 self._link_interface(system_id, iface_id)
370 return response
371
372
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100373class BootResource(MaasObject):
374 def __init__(self):
375 super(BootResource, self).__init__()
376 self._all_elements_url = u'api/2.0/boot-resources/'
377 self._create_url = u'api/2.0/boot-resources/'
378 self._update_url = u'api/2.0/boot-resources/{0}/'
379 self._config_path = 'region.boot_resources'
380
381 def fill_data(self, name, boot_data):
382 sha256 = hashlib.sha256()
383 sha256.update(file(boot_data['content']).read())
384 data = {
385 'name': name,
386 'title': boot_data['title'],
387 'architecture': boot_data['architecture'],
388 'filetype': boot_data['filetype'],
389 'size': str(os.path.getsize(boot_data['content'])),
390 'sha256': sha256.hexdigest(),
391 'content': io.open(boot_data['content']),
392 }
393 return data
394
395 def update(self, new, old):
396 self._update = False
397 return new
398
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100399class CommissioningScripts(MaasObject):
400 def __init__(self):
401 super(CommissioningScripts, self).__init__()
402 self._all_elements_url = u'api/2.0/commissioning-scripts/'
403 self._create_url = u'api/2.0/commissioning-scripts/'
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100404 self._config_path = 'region.commissioning_scripts'
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100405 self._update_url = u'api/2.0/commissioning-scripts/{0}'
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100406 self._update_key = 'name'
407
408 def fill_data(self, name, file_path):
409 data = {
410 'name': name,
411 'content': io.open(file_path),
412 }
413 return data
414
415 def update(self, new, old):
416 return new
417
418class MaasConfig(MaasObject):
419 def __init__(self):
420 super(MaasConfig, self).__init__()
421 self._all_elements_url = None
422 self._create_url = (u'api/2.0/maas/', u'set_config')
423 self._config_path = 'region.maas_config'
424
425 def fill_data(self, name, value):
426 data = {
427 'name': name,
Krzysztof Szukiełojća6352a42017-03-17 14:21:57 +0100428 'value': str(value),
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100429 }
430 return data
431
432 def update(self, new, old):
433 self._update = False
434 return new
435
436
Krzysztof Szukiełojć8cc32b42017-03-29 15:22:57 +0200437#class SSHPrefs(MaasObject):
438# def __init__(self):
439# super(SSHPrefs, self).__init__()
440# self._all_elements_url = u'api/2.0/machines/'
441# self._create_url = u'api/2.0/account/prefs/sshkeys/'
442# self._config_path = 'region.sshprefs'
443# self._element_key = 'hostname'
444# self._update_key = 'system_id'
445#
446# def fill_data(self, value):
447# data = {
448# 'key': value,
449# }
450# return data
451#
452# def process(self):
453# config = __salt__['config.get']('maas')
454# for part in self._config_path.split('.'):
455# config = config.get(part, {})
456# extra = {}
457# for name, url_call in self._extra_data_urls.iteritems():
458# key = 'id'
459# if isinstance(url_call, tuple):
460# url_call, key = url_call[:]
461# extra[name] = {v['name']: v[key] for v in
462# json.loads(self._maas.get(url_call).read())}
463# if self._all_elements_url:
464# all_elements = {}
465# elements = self._maas.get(self._all_elements_url).read()
466# res_json = json.loads(elements)
467# for element in res_json:
468# if isinstance(element, (str, unicode)):
469# all_elements[element] = {}
470# else:
471# all_elements[element[self._element_key]] = element
472# else:
473# all_elements = {}
474# ret = {
475# 'success': [],
476# 'errors': {},
477# 'updated': [],
478# }
479# for config_data in config:
480# try:
481# data = self.fill_data(config_data, **extra)
482# self.send(data)
483# ret['success'].append(name)
484# except urllib2.HTTPError as e:
485# etxt = e.read()
486# LOG.exception('Failed for object %s reason %s', name, etxt)
487# ret['errors'][name] = str(etxt)
488# except Exception as e:
489# LOG.exception('Failed for object %s reason %s', name, e)
490# ret['errors'][name] = str(e)
491# if ret['errors']:
492# raise Exception(ret)
493# return ret
494
495class Domain(MaasObject):
496 def __init__(self):
497 super(Domain, self).__init__()
498 self._all_elements_url = u'/api/2.0/domains/'
499 self._create_url = u'/api/2.0/domains/'
500 self._config_path = 'region.domain'
501 self._update_url = u'/api/2.0/domains/{0}/'
502
503 def fill_data(self, value):
504 data = {
505 'name': value,
506 }
507 self._update = True
508 return data
509
510 def update(self, new, old):
511 new['id'] = str(old['id'])
512 new['authoritative'] = str(old['authoritative'])
513 return new
514
515 def process(self):
516 config = __salt__['config.get']('maas')
517 for part in self._config_path.split('.'):
518 config = config.get(part, {})
519 extra = {}
520 for name, url_call in self._extra_data_urls.iteritems():
521 key = 'id'
522 if isinstance(url_call, tuple):
523 url_call, key = url_call[:]
524 extra[name] = {v['name']: v[key] for v in
525 json.loads(self._maas.get(url_call).read())}
526 if self._all_elements_url:
527 all_elements = {}
528 elements = self._maas.get(self._all_elements_url).read()
529 res_json = json.loads(elements)
530 for element in res_json:
531 if isinstance(element, (str, unicode)):
532 all_elements[element] = {}
533 else:
534 all_elements[element[self._element_key]] = element
535 else:
536 all_elements = {}
537 ret = {
538 'success': [],
539 'errors': {},
540 'updated': [],
541 }
542 try:
543 data = self.fill_data(config, **extra)
544 data = self.update(data, all_elements.values()[0])
545 self.send(data)
546 ret['success'].append('domain')
547 except urllib2.HTTPError as e:
548 etxt = e.read()
549 LOG.exception('Failed for object %s reason %s', 'domain', etxt)
550 ret['errors']['domain'] = str(etxt)
551 except Exception as e:
552 LOG.exception('Failed for object %s reason %s', 'domain', e)
553 ret['errors']['domain'] = str(e)
554 if ret['errors']:
555 raise Exception(ret)
556 return ret
557
558
Krzysztof Szukiełojćc4b33092017-02-15 13:25:38 +0100559def process_fabrics():
560 return Fabric().process()
561
562def process_subnets():
563 return Subnet().process()
564
565def process_dhcp_snippets():
566 return DHCPSnippet().process()
567
568def process_package_repositories():
569 return PacketRepository().process()
570
571def process_devices():
572 return Device().process()
573
574def process_machines():
575 return Machine().process()
576
577def process_boot_resources():
578 return BootResource().process()
Krzysztof Szukiełojć43bc7e02017-03-17 10:32:07 +0100579
580def process_maas_config():
581 return MaasConfig().process()
582
583def process_commissioning_scripts():
584 return CommissioningScripts().process()
Krzysztof Szukiełojć8cc32b42017-03-29 15:22:57 +0200585
586def process_domain():
587 return Domain().process()