blob: 4002e05dff733cde963859f60d3da71b5e871293 [file] [log] [blame]
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001# -*- coding: utf-8 -*-
azvyagintsevbca1f462018-05-25 19:06:46 +03002"""
Ondrej Smolab57a23b2018-01-24 11:18:24 +01003Module 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
azvyagintsevbca1f462018-05-25 19:06:46 +030012"""
Ondrej Smolab57a23b2018-01-24 11:18:24 +010013
14from __future__ import absolute_import
15
16import collections
17import copy
18import hashlib
19import io
20import json
21import logging
Ondrej Smolab57a23b2018-01-24 11:18:24 +010022import time
23import urllib2
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020024# Salt utils
Ondrej Smolab57a23b2018-01-24 11:18:24 +010025from salt.exceptions import CommandExecutionError, SaltInvocationError
26
27LOG = logging.getLogger(__name__)
28
29SIZE = {
30 "M": 1000000,
31 "G": 1000000000,
32 "T": 1000000000000,
33}
34
35RAID = {
36 0: "raid-0",
37 1: "raid-1",
38 5: "raid-5",
39 10: "raid-10",
40}
41
42# Import third party libs
Alexandr Lovtsov4fc89602019-02-22 18:46:59 +030043try:
44 import netaddr
45 HAS_NETADDR = True
46except ImportError:
47 HAS_NETADDR = False
48
Ondrej Smolab57a23b2018-01-24 11:18:24 +010049try:
50 from maas_client import MAASClient, MAASDispatcher, MAASOAuth
51 HAS_MASS = True
52except ImportError:
Alexandr Lovtsov4fc89602019-02-22 18:46:59 +030053 HAS_MASS = False
Ondrej Smolab57a23b2018-01-24 11:18:24 +010054
55
56def __virtual__():
azvyagintsevbca1f462018-05-25 19:06:46 +030057 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +010058 Only load this module if maas-client
59 is installed on this minion.
azvyagintsevbca1f462018-05-25 19:06:46 +030060 """
Alexandr Lovtsov4fc89602019-02-22 18:46:59 +030061 if not HAS_NETADDR:
62 return False, "'netaddr' python library is unavailable"
63 if not HAS_MASS:
64 return False, "MaaS client library is unavailable"
65 return 'maasng'
Ondrej Smolab57a23b2018-01-24 11:18:24 +010066
67
68APIKEY_FILE = '/var/lib/maas/.maas_credentials'
69
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020070
Ondrej Smolab57a23b2018-01-24 11:18:24 +010071def _format_data(data):
72 class Lazy:
73 def __str__(self):
74 return ' '.join(['{0}={1}'.format(k, v)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020075 for k, v in data.iteritems()])
Ondrej Smolab57a23b2018-01-24 11:18:24 +010076 return Lazy()
77
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020078
azvyagintsev58947072018-06-29 12:09:48 +030079def _create_maas_client(api_url=None):
80 if not api_url:
81 api_url = 'http://localhost:5240/MAAS'
Ondrej Smolab57a23b2018-01-24 11:18:24 +010082 global APIKEY_FILE
83 try:
84 api_token = file(APIKEY_FILE).read().splitlines()[-1].strip()\
85 .split(':')
86 except:
87 LOG.exception('token')
88 auth = MAASOAuth(*api_token)
Ondrej Smolab57a23b2018-01-24 11:18:24 +010089 dispatcher = MAASDispatcher()
90 return MAASClient(auth, dispatcher, api_url)
91
Ondrej Smolab57a23b2018-01-24 11:18:24 +010092
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020093def _get_blockdevice_id_by_name(hostname, device):
94
95 # TODO validation
Ondrej Smolab57a23b2018-01-24 11:18:24 +010096 return list_blockdevices(hostname)[device]["id"]
97
Ondrej Smolab57a23b2018-01-24 11:18:24 +010098
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020099def _get_volume_group_id_by_name(hostname, device):
100
101 # TODO validation
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100102 return list_volume_groups(hostname)[device]["id"]
103
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200104
azvyagintsevbca1f462018-05-25 19:06:46 +0300105def _get_volume_id_by_name(hostname, volume_name, volume_group, maas_volname=True):
106
107 if not maas_volname:
108 # MAAS-like name
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200109 volume_name = str("%s-%s" % (volume_group, volume_name))
110 # TODO validation
azvyagintsevbca1f462018-05-25 19:06:46 +0300111 return get_volumes(hostname, volume_group)[volume_name]["id"]
112
113
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100114def _get_partition_id_by_name(hostname, device, partition):
115
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200116 # TODO validation
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100117 return list_partitions(hostname, device)[partition]["id"]
118
Dzmitry Stremkouskid95bd2e2018-12-03 17:35:46 +0100119def is_valid_ipv4(address):
120 """Verify that address represents a valid IPv4 address.
121 :param address: Value to verify
122 :type address: string
123 :returns: bool
124 .. versionadded:: 1.1
125 """
126 try:
127 return netaddr.valid_ipv4(address)
128 except netaddr.AddrFormatError:
129 return False
130
131def is_valid_ipv6(address):
132 """Verify that address represents a valid IPv6 address.
133 :param address: Value to verify
134 :type address: string
135 :returns: bool
136 .. versionadded:: 1.1
137 """
138 if not address:
139 return False
140
141 parts = address.rsplit("%", 1)
142 address = parts[0]
143 scope = parts[1] if len(parts) > 1 else None
144 if scope is not None and (len(scope) < 1 or len(scope) > 15):
145 return False
146
147 try:
148 return netaddr.valid_ipv6(address, netaddr.core.INET_PTON)
149 except netaddr.AddrFormatError:
150 return False
151
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200152# MACHINE SECTION
153
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100154
155def get_machine(hostname):
azvyagintsevbca1f462018-05-25 19:06:46 +0300156 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100157 Get information aboout specified machine
158
159 CLI Example:
160
161 .. code-block:: bash
162
163 salt-call maasng.get_machine server_hostname
Alexei Lugovoie5b64122018-11-06 12:30:01 +0100164
165 Error codes:
166 0 : Machine not found
azvyagintsevbca1f462018-05-25 19:06:46 +0300167 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100168 try:
169 return list_machines()[hostname]
170 except KeyError:
Alexei Lugovoie5b64122018-11-06 12:30:01 +0100171 return {"error":
172 { 0: "Machine not found" }
173 }
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100174
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200175
Alexandru Avadanii48c51f42018-09-22 20:14:10 +0200176def list_machines(status_filter=None):
azvyagintsevbca1f462018-05-25 19:06:46 +0300177 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100178 Get list of all machines from maas server
179
180 CLI Example:
181
182 .. code-block:: bash
183
184 salt 'maas-node' maasng.list_machines
Alexandru Avadanii48c51f42018-09-22 20:14:10 +0200185 salt 'maas-node' maasng.list_machines status_filter=[Deployed,Ready]
azvyagintsevbca1f462018-05-25 19:06:46 +0300186 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200187 machines = {}
188 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100189 json_res = json.loads(maas.get(u'api/2.0/machines/').read())
190 for item in json_res:
Alexandru Avadanii48c51f42018-09-22 20:14:10 +0200191 if not status_filter or item['status_name'] in status_filter:
192 machines[item["hostname"]] = item
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100193 return machines
194
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200195
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100196def create_machine():
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200197 # TODO
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100198
199 return False
200
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200201
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100202def update_machine():
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200203 # TODO
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100204
205 return False
206
Alexandru Avadanii48c51f42018-09-22 20:14:10 +0200207def delete_machine(hostname):
208 """
209 Delete specified machine
210
211 CLI Example:
212
213 .. code-block:: bash
214
215 salt 'maas-node' maasng.delete_machine server_hostname
216 salt-call maasng.delete_machine server_hostname
217 """
218 result = {}
219 maas = _create_maas_client()
220 system_id = get_machine(hostname)["system_id"]
221 LOG.debug('delete_machine: {}'.format(system_id))
222 maas.delete(
223 u"api/2.0/machines/{0}/".format(system_id)).read()
224
225 result["new"] = "Machine {0} deleted".format(hostname)
226 return result
227
Dzmitry Stremkouskid95bd2e2018-12-03 17:35:46 +0100228def machine_power_state(hostname):
229 """
230 Query the power state of a node.
231
232 :param hostname: Node hostname
233
234 CLI Example:
235
236 .. code-block:: bash
237
238 salt 'maas-node' maasng.machine_power_state kvm06
239
240 """
241 result = {}
242 maas = _create_maas_client()
243 system_id = get_machine(hostname)["system_id"]
244 LOG.debug('action_machine: {}'.format(system_id))
245
246 # TODO validation
247 json_res = json.loads(maas.get(
248 u"api/2.0/machines/{0}/".format(system_id), "query_power_state").read())
249 LOG.info(json_res)
250
251 return json_res
252
Alexandru Avadanii48c51f42018-09-22 20:14:10 +0200253def action_machine(hostname, action, comment=None):
254 """
255 Send simple action (e.g. mark_broken, mark_fixed) to machine.
256
257 :param action: Action to send for machine (one of MaaS' op codes)
258 :param comment: Optional comment for the event log.
259
260 CLI Example:
261
262 .. code-block:: bash
263
264 salt 'maas-node' maasng.action_machine server_hostname mark_broken comment='dead'
265 """
266 result = {}
267 data = {}
268 maas = _create_maas_client()
269 system_id = get_machine(hostname)["system_id"]
270 LOG.debug('action_machine: {}'.format(system_id))
271
272 # TODO validation
273 if comment:
274 data["comment"] = comment
275 json_res = json.loads(maas.post(
276 u"api/2.0/machines/{0}/".format(system_id), action, **data).read())
277 LOG.info(json_res)
278 result["new"] = "Machine {0} action {1} executed".format(hostname, action)
279
280 return result
281
azvyagintsevbca1f462018-05-25 19:06:46 +0300282# END MACHINE SECTION
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200283# RAID SECTION
284
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100285
286def create_raid(hostname, name, level, disks=[], partitions=[], **kwargs):
azvyagintsevbca1f462018-05-25 19:06:46 +0300287 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100288 Create new raid on machine.
289
290 CLI Example:
291
292 .. code-block:: bash
293
294 salt-call maasng.create_raid hostname=kvm03 name=md0 level=1 disks=[vdb,vdc] partitions=[vdd-part1,vde-part1]
azvyagintsevbca1f462018-05-25 19:06:46 +0300295 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100296
297 result = {}
298
299 if len(disks) == 0 and len(partitions) == 0:
300 result["error"] = "Disks or partitions need to be provided"
301
302 disk_ids = []
303 partition_ids = []
304
305 for disk in disks:
306 try:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200307 disk_ids.append(str(_get_blockdevice_id_by_name(hostname, disk)))
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100308 except KeyError:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200309 result["error"] = "Device {0} does not exists on machine {1}".format(
310 disk, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100311 return result
312
313 for partition in partitions:
314 try:
315 device = partition.split("-")[0]
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200316 device_part = list_partitions(hostname, device)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100317 partition_ids.append(str(device_part[partition]["id"]))
318 except KeyError:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200319 result["error"] = "Partition {0} does not exists on machine {1}".format(
320 partition, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100321 return result
322
323 data = {
324 "name": name,
325 "level": RAID[int(level)],
326 "block_devices": disk_ids,
327 "partitions": partition_ids,
328 }
329
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200330 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100331 system_id = get_machine(hostname)["system_id"]
332 LOG.info(system_id)
333
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200334 # TODO validation
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100335 LOG.info(data)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200336 json_res = json.loads(
337 maas.post(u"api/2.0/nodes/{0}/raids/".format(system_id), None, **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100338 LOG.info(json_res)
339 result["new"] = "Raid {0} created".format(name)
340
341 return result
342
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200343
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100344def list_raids(hostname):
azvyagintsevbca1f462018-05-25 19:06:46 +0300345 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100346 Get list all raids on machine
347
348 CLI Example:
349
350 .. code-block:: bash
351
352 salt-call maasng.list_raids server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300353 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100354
azvyagintsevbca1f462018-05-25 19:06:46 +0300355 raids = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200356 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100357 system_id = get_machine(hostname)["system_id"]
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200358 # TODO validation
359 json_res = json.loads(
360 maas.get(u"api/2.0/nodes/{0}/raids/".format(system_id)).read())
azvyagintsevbca1f462018-05-25 19:06:46 +0300361 LOG.debug('list_raids:{} {}'.format(system_id, json_res))
362 for item in json_res:
363 raids[item["name"]] = item
364 return raids
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100365
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200366
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100367def get_raid(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300368 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100369 Get information about specific raid on machine
370
371 CLI Example:
372
373 .. code-block:: bash
374
375 salt-call maasng.get_raids server_hostname md0
azvyagintsevbca1f462018-05-25 19:06:46 +0300376 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100377
378 return list_raids(hostname)[name]
379
380
azvyagintsevbca1f462018-05-25 19:06:46 +0300381def _get_raid_id_by_name(hostname, raid_name):
382 return get_raid(hostname, raid_name)['id']
383
384
385def delete_raid(hostname, raid_name):
386 """
387 Delete RAID on a machine.
388
389 CLI Example:
390
391 .. code-block:: bash
392
393 salt 'maas-node' maasng.delete_raid server_hostname raid_name
394 salt-call maasng.delete_raid server_hostname raid_name
395 """
396 result = {}
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200397 maas = _create_maas_client()
azvyagintsevbca1f462018-05-25 19:06:46 +0300398 system_id = get_machine(hostname)["system_id"]
399 raid_id = _get_raid_id_by_name(hostname, raid_name)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200400 LOG.debug('delete_raid: {} {}'.format(system_id, raid_id))
401 maas.delete(
402 u"api/2.0/nodes/{0}/raid/{1}/".format(system_id, raid_id)).read()
azvyagintsevbca1f462018-05-25 19:06:46 +0300403
404 result["new"] = "Raid {0} deleted".format(raid_name)
405 return result
406
407# END RAID SECTION
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200408# BLOCKDEVICES SECTION
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100409
azvyagintsevbca1f462018-05-25 19:06:46 +0300410
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100411def list_blockdevices(hostname):
azvyagintsevbca1f462018-05-25 19:06:46 +0300412 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100413 Get list of all blockdevices (disks) on machine
414
415 CLI Example:
416
417 .. code-block:: bash
418
419 salt 'maas-node' maasng.list_blockdevices server_hostname
420 salt-call maasng.list_blockdevices server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300421 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100422 ret = {}
423
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200424 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100425 system_id = get_machine(hostname)["system_id"]
426 LOG.info(system_id)
427
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200428 # TODO validation if exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100429
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200430 json_res = json.loads(
431 maas.get(u"api/2.0/nodes/{0}/blockdevices/".format(system_id)).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100432 LOG.info(json_res)
433 for item in json_res:
434 ret[item["name"]] = item
435
436 return ret
437
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200438
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100439def get_blockdevice(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300440 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100441 Get information about blockdevice (disk) on machine
442
443 CLI Example:
444
445 .. code-block:: bash
446
447 salt 'maas-node' maasng.get_blockdevice server_hostname sda
448 salt-call maasng.get_blockdevice server_hostname sda
azvyagintsevbca1f462018-05-25 19:06:46 +0300449 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100450
451 return list_blockdevices(hostname)[name]
452
azvyagintsevbca1f462018-05-25 19:06:46 +0300453# END BLOCKDEVICES SECTION
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200454# PARTITIONS
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100455
azvyagintsevbca1f462018-05-25 19:06:46 +0300456
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100457def list_partitions(hostname, device):
azvyagintsevbca1f462018-05-25 19:06:46 +0300458 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100459 Get list of all partitions on specific device located on specific machine
460
461 CLI Example:
462
463 .. code-block:: bash
464
465 salt 'maas-node' maasng.list_partitions server_hostname sda
466 salt-call maasng.list_partitions server_hostname sda
azvyagintsevbca1f462018-05-25 19:06:46 +0300467 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100468 ret = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200469 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100470 system_id = get_machine(hostname)["system_id"]
471 LOG.info(system_id)
472
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200473 partitions = get_blockdevice(hostname, device)["partitions"]
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100474 LOG.info(partitions)
475
476 #json_res = json.loads(maas.get(u"api/2.0/nodes/{0}/blockdevices/{1}/partitions/".format(system_id, device_id)).read())
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200477 # LOG.info(json_res)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100478
479 if len(device) > 0:
480 for item in partitions:
481 name = item["path"].split('/')[-1]
482 ret[name] = item
483
484 return ret
485
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200486
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100487def get_partition(hostname, device, partition):
azvyagintsevbca1f462018-05-25 19:06:46 +0300488 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100489 Get information about specific parition on device located on machine
490
491 CLI Example:
492
493 .. code-block:: bash
494
495 salt 'maas-node' maasng.get_partition server_hostname disk_name partition
496 salt-call maasng.get_partition server_hostname disk_name partition
497
498 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300499 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100500
501 return list_partitions(partition)[name]
502
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200503
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100504def create_partition(hostname, disk, size, fs_type=None, mount=None):
azvyagintsevbca1f462018-05-25 19:06:46 +0300505 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100506 Create new partition on device.
507
508 CLI Example:
509
510 .. code-block:: bash
511
512 salt 'maas-node' maasng.create_partition server_hostname disk_name 10 ext4 "/"
513 salt-call maasng.create_partition server_hostname disk_name 10 ext4 "/"
azvyagintsevbca1f462018-05-25 19:06:46 +0300514 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200515 # TODO validation
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100516 result = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200517 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100518 system_id = get_machine(hostname)["system_id"]
519 LOG.info(system_id)
520
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200521 device_id = _get_blockdevice_id_by_name(hostname, disk)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100522 LOG.info(device_id)
523
524 value, unit = size[:-1], size[-1]
525 calc_size = str(int(value) * SIZE[unit])
526 LOG.info(calc_size)
527
528 data = {
529 "size": calc_size
530 }
531
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200532 # TODO validation
533 partition = json.loads(maas.post(
534 u"api/2.0/nodes/{0}/blockdevices/{1}/partitions/".format(system_id, device_id), None, **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100535 LOG.info(partition)
536 result["partition"] = "Partition created on {0}".format(disk)
537
538 if fs_type != None:
539 data_fs_type = {
540 "fstype": fs_type
541 }
542 partition_id = str(partition["id"])
543 LOG.info("Partition id: " + partition_id)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200544 # TODO validation
545 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
546 system_id, device_id, partition_id), "format", **data_fs_type).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100547 LOG.info(json_res)
548 result["filesystem"] = "Filesystem {0} created".format(fs_type)
549
550 if mount != None:
551 data = {
552 "mount_point": mount
553 }
554
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200555 # TODO validation
556 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
557 system_id, device_id, str(partition['id'])), "mount", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100558 LOG.info(json_res)
559 result["mount"] = "Mount point {0} created".format(mount)
560
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100561 return result
562
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200563
Dzmitry Stremkouskid95bd2e2018-12-03 17:35:46 +0100564def list_dnsresources():
565 """
566 List DNS resources known to MAAS.
567
568 CLI Example:
569
570 .. code-block:: bash
571
572 salt-call maasng.list_dnsresources
573
574 """
575 result = {}
576 res_json = []
577 maas = _create_maas_client()
578
579 # TODO validation
580 result = json.loads(maas.get(u"api/2.0/dnsresources/").read())
581 for elem in result:
582 ip_addresses = []
583 for ip in elem["ip_addresses"]:
584 ip_addresses.append(ip["ip"])
585 res_json.append(
586 {
587 "ip_addresses": ip_addresses,
588 "id": elem["id"],
589 "fqdn": elem["fqdn"],
590 "hostname": elem["fqdn"].split(".")[0]
591 }
592 )
593
594 LOG.debug(res_json)
595
596 return res_json
597
598
599def list_ipaddresses():
600 """
601 List IP addresses known to MAAS.
602
603 CLI Example:
604
605 .. code-block:: bash
606
607 salt-call maasng.list_ipaddresses
608
609 """
610 result = {}
611 res_json = []
612 maas = _create_maas_client()
613
614 # TODO validation
615 result = json.loads(maas.get(u"api/2.0/ipaddresses/?all").read())
616 for elem in result:
617 res_json.append(
618 {
619 "ip": elem["ip"],
620 "owner": { "username": elem["owner"]["username"] },
621 "created": elem["created"],
622 "alloc_type_name": elem["alloc_type_name"],
623 "alloc_type": elem["alloc_type"],
624 "subnet": {
625 "id": elem["subnet"]["id"],
626 "cidr": elem["subnet"]["cidr"],
627 "name": elem["subnet"]["name"]
628 }
629 }
630 )
631
632 LOG.debug(res_json)
633
634 return res_json
635
636
637def reserve_ipaddress(hostname,subnet,ip=""):
638 """
639 Reserve IP address for specified hostname in specified subnet
640
641 CLI Example:
642
643 .. code-block:: bash
644
645 salt-call maasng.reserve_ipaddress hostname 192.168.0.0/24 192.168.0.254
646 salt-call maasng.reserve_ipaddress hostname 192.168.0.0/24
647
648 """
649 result = {}
650 data = {}
651 maas = _create_maas_client()
652
653 data = {
654 "subnet": subnet,
655 "hostname": hostname
656 }
657
658 if ip:
659 data["ip"] = ip
660
661 # TODO validation
662 result = json.loads(maas.post(u"api/2.0/ipaddresses/", "reserve", **data).read())
663 res_json = {
664 "created": result["created"],
665 "type": "DNS",
666 "hostname": hostname,
667 "ip": result["ip"]
668 }
669
670 LOG.info(res_json)
671
672 return res_json
673
674
675def release_ipaddress(ipaddress):
676 """
677 Release an IP address that was previously reserved by the user.
678
679 CLI Example:
680
681 .. code-block:: bash
682
683 salt-call maasng.release_ipaddress 192.168.2.10
684
685 """
686 result = {}
687 data = {}
688 maas = _create_maas_client()
689
690 data = {
691 "ip": ipaddress
692 }
693
694 # TODO validation
695 return maas.post(u"api/2.0/ipaddresses/", "release", **data).read()
696
697
698def sync_address_pool():
699 """
700 Manage address pool for ext_pillar.
701
702 CLI Example:
703
704 .. code-block:: bash
705
706 salt-call maasng.sync_address_pool
707
708 """
709
710 address_pool = __pillar__["address_pool"]
711 LOG.debug("Address pool:")
712 LOG.debug(address_pool)
713
714 cluster_networks = __pillar__["cluster_networks"]
715 LOG.debug("Cluster networks:")
716 LOG.debug(cluster_networks)
717
718 dnsresources = list_dnsresources()
719 LOG.debug("DNS resources:")
720 LOG.debug(dnsresources)
721
722 machines = list_machines()
723 LOG.debug("Machines:")
724 LOG.debug(machines)
725
726 for net in address_pool:
727 if net == "external":
728 continue
729 for addr in address_pool[net]['pool']:
730 ipaddr = address_pool[net]['pool'][addr]
731 if ipaddr == "":
732 LOG.debug('Releasing IP address for: ' + addr)
733 release_required = False
734 for elem in dnsresources:
735 if elem["hostname"] == addr:
736 release_required = True
737 ip_addresses = elem["ip_addresses"]
738 if release_required:
739 for ip in ip_addresses:
740 res = release_ipaddress(ip)
741 LOG.debug(res)
742 else:
743 LOG.debug('IP for ' + addr + ' already released')
744 elif is_valid_ipv6(ipaddr) or is_valid_ipv4(ipaddr):
745 LOG.debug('Ensure static IP address "' + ipaddr + '" for ' + addr)
746 reserve_required = True
747 for elem in dnsresources:
748 if elem["hostname"] == addr:
749 reserve_required = False
750 for elem, elemval in machines.iteritems():
751 for iface in elemval["interface_set"]:
752 for link in iface["links"]:
753 if "ip_address" in link:
754 if link["ip_address"] == ipaddr:
755 reserve_required = False
756 if reserve_required:
757 res = reserve_ipaddress(addr, cluster_networks[net]['cidr'], ipaddr)
758 reserve_required = False
759 LOG.debug(res)
760 else:
761 LOG.debug('Static IP address "' + ipaddr + '" for ' + addr + ' ensured')
762 else:
763 LOG.debug('Requesting IP address for' + addr)
764 reserve_required = True
765 for elem in dnsresources:
766 if elem["hostname"] == addr:
767 reserve_required = False
768 ip = elem["ip_addresses"][0]
769 if reserve_required:
770 res = reserve_ipaddress(addr, cluster_networks[net]['cidr'])
771 LOG.debug(res)
772 else:
773 LOG.debug(addr + " already has IP " + ip)
774
775 return True
776
777
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100778def delete_partition(hostname, disk, partition_name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300779 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100780 Delete partition on device.
781
782 CLI Example:
783
784 .. code-block:: bash
785
786 salt 'maas-node' maasng.delete_partition server_hostname disk_name partition_name
787 salt-call maasng.delete_partition server_hostname disk_name partition_name
788
789 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300790 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100791 result = {}
792 data = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200793 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100794 system_id = get_machine(hostname)["system_id"]
795 LOG.info(system_id)
796
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200797 device_id = _get_blockdevice_id_by_name(hostname, disk)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100798 LOG.info(device_id)
799
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200800 partition_id = _get_partition_id_by_name(hostname, disk, partition_name)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100801
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200802 maas.delete(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
803 system_id, device_id, partition_id)).read()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100804 result["new"] = "Partition {0} deleted".format(partition_name)
805 return result
806
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200807
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100808def delete_partition_by_id(hostname, disk, partition_id):
azvyagintsevbca1f462018-05-25 19:06:46 +0300809 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100810 Delete partition on device. Partition spefified by id of parition
811
812 CLI Example:
813
814 .. code-block:: bash
815
816 salt 'maas-node' maasng.delete_partition_by_id server_hostname disk_name partition_id
817 salt-call maasng.delete_partition_by_id server_hostname disk_name partition_id
818
819 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300820 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100821 result = {}
822 data = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200823 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100824 system_id = get_machine(hostname)["system_id"]
825 LOG.info(system_id)
826
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200827 device_id = _get_blockdevice_id_by_name(hostname, disk)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100828 LOG.info(device_id)
829
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200830 maas.delete(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
831 system_id, device_id, partition_id)).read()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100832 result["new"] = "Partition {0} deleted".format(partition_id)
833 return result
azvyagintsevbca1f462018-05-25 19:06:46 +0300834# END PARTITIONS
835# DISK LAYOUT
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100836
azvyagintsevbca1f462018-05-25 19:06:46 +0300837
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200838def drop_storage_schema(hostname, disk=None):
azvyagintsevbca1f462018-05-25 19:06:46 +0300839 """
840 #1. Drop lv
841 #2. Drop vg
842 #3. Drop md # need to zero-block?
843 #3. Drop part
844 """
845
846 if __opts__['test']:
847 ret['result'] = None
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200848 ret['comment'] = 'Storage schema on {0} will be removed'.format(
849 hostname)
azvyagintsevbca1f462018-05-25 19:06:46 +0300850 return ret
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200851 # TODO validation if exists
azvyagintsevbca1f462018-05-25 19:06:46 +0300852 vgs = list_volume_groups(hostname)
853 for vg in vgs:
854 delete_volume_group(hostname, vg)
855
856 raids = list_raids(hostname)
857 for raid in raids:
858 delete_raid(hostname, raid)
859
860 blocks = list_blockdevices(hostname)
861 for block_d in blocks:
862 partitions = __salt__['maasng.list_partitions'](hostname, block_d)
863 for partition_name, partition in partitions.iteritems():
864 LOG.info('delete partition:\n{}'.format(partition))
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200865 __salt__['maasng.delete_partition_by_id'](
866 hostname, block_d, partition["id"])
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200867
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100868
869def update_disk_layout(hostname, layout, root_size=None, root_device=None, volume_group=None, volume_name=None, volume_size=None):
azvyagintsevbca1f462018-05-25 19:06:46 +0300870 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100871 Update disk layout. Flat or LVM layout supported.
872
873 CLI Example:
874
875 .. code-block:: bash
876
877 salt 'maas-node' maasng.update_disk_layout server_hostname lvm root_size=None, root_device=None, volume_group=None, volume_name=None, volume_size=None
878 salt-call maasng.update_disk_layout server_hostname lvm root_size=None, root_device=None, volume_group=None, volume_name=None, volume_size=None
879
880 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300881 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100882 result = {}
883 data = {
884 "storage_layout": layout,
885 }
886
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200887 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100888 system_id = get_machine(hostname)["system_id"]
889 LOG.info(system_id)
890
azvyagintsevbca1f462018-05-25 19:06:46 +0300891 if layout == 'custom':
892 drop_storage_schema(hostname)
893 result["new"] = {
894 "storage_layout": layout,
895 }
896
897 return result
898
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100899 if root_size != None:
900 bit_size = str(root_size * 1073741824)
901 LOG.info(bit_size)
902 data["root_size"] = bit_size
903
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100904 if root_device != None:
905 LOG.info(root_device)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200906 data["root_device"] = str(
907 _get_blockdevice_id_by_name(hostname, root_device))
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100908
909 if layout == 'lvm':
910 if volume_group != None:
911 LOG.info(volume_group)
912 data["vg_name"] = volume_group
913 if volume_name != None:
914 LOG.info(volume_name)
915 data["lv_name"] = volume_name
916 if volume_size != None:
917 vol_size = str(volume_size * 1073741824)
918 LOG.info(vol_size)
919 data["lv_size"] = vol_size
920
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200921 # TODO validation
922 json_res = json.loads(maas.post(
923 u"api/2.0/machines/{0}/".format(system_id), "set_storage_layout", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100924 LOG.info(json_res)
925 result["new"] = {
926 "storage_layout": layout,
927 }
928
929 return result
930
azvyagintsevbca1f462018-05-25 19:06:46 +0300931# END DISK LAYOUT
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200932# LVM
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100933
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200934
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100935def list_volume_groups(hostname):
azvyagintsevbca1f462018-05-25 19:06:46 +0300936 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100937 Get list of all volume group on machine.
938
939 CLI Example:
940
941 .. code-block:: bash
942
943 salt 'maas-node' maasng.list_volume_groups server_hostname
944 salt-call maasng.list_volume_groups server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300945 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100946 volume_groups = {}
947
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200948 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100949 system_id = get_machine(hostname)["system_id"]
950 LOG.info(system_id)
951
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200952 # TODO validation if exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100953
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200954 json_res = json.loads(
955 maas.get(u"api/2.0/nodes/{0}/volume-groups/".format(system_id)).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100956 LOG.info(json_res)
957 for item in json_res:
958 volume_groups[item["name"]] = item
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200959 # return
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100960 return volume_groups
961
962
963def get_volume_group(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300964 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100965 Get information about specific volume group on machine.
966
967 CLI Example:
968
969 .. code-block:: bash
970
971 salt 'maas-node' maasng.list_blockdevices server_hostname
972 salt-call maasng.list_blockdevices server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300973 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200974 # TODO validation that exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100975 return list_volume_groups(hostname)[name]
976
977
978def create_volume_group(hostname, volume_group_name, disks=[], partitions=[]):
azvyagintsevbca1f462018-05-25 19:06:46 +0300979 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100980 Create new volume group on machine. Disks or partitions needs to be provided.
981
982 CLI Example:
983
984 .. code-block:: bash
985
986 salt 'maas-node' maasng.create_volume_group volume_group_name, disks=[sda,sdb], partitions=[]
987 salt-call maasng.create_volume_group server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300988 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100989 result = {}
990
991 data = {
992 "name": volume_group_name,
993 }
994
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200995 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100996 system_id = get_machine(hostname)["system_id"]
997 LOG.info(system_id)
998
999 disk_ids = []
1000 partition_ids = []
1001
1002 for disk in disks:
1003 p_disk = get_blockdevice(hostname, disk)
1004 if p_disk["partition_table_type"] == None:
1005 disk_ids.append(str(p_disk["id"]))
1006 else:
azvyagintsevf3515c82018-06-26 18:59:05 +03001007 result["error"] = "Device {0} on" \
1008 "machine {1} cointains partition" \
1009 "table".format(disk, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001010 return result
1011
1012 for partition in partitions:
1013 try:
1014 device = partition.split("-")[0]
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001015 device_part = list_partitions(hostname, device)
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001016 partition_ids.append(str(device_part[partition]["id"]))
1017 except KeyError:
azvyagintsevf3515c82018-06-26 18:59:05 +03001018 result["error"] = "Partition {0} does" \
1019 "not exists on " \
1020 "machine {1}".format(partition, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001021 return result
1022
1023 data["block_devices"] = disk_ids
1024 data["partitions"] = partition_ids
1025 LOG.info(partition_ids)
1026 LOG.info(partitions)
1027
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001028 # TODO validation
1029 json_res = json.loads(maas.post(
1030 u"api/2.0/nodes/{0}/volume-groups/".format(system_id), None, **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001031 LOG.info(json_res)
1032 result["new"] = "Volume group {0} created".format(json_res["name"])
1033
1034 return result
1035
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001036
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001037def delete_volume_group(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +03001038 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001039 Delete volume group on machine.
1040
1041 CLI Example:
1042
1043 .. code-block:: bash
1044
1045 salt 'maas-node' maasng.delete_volume_group server_hostname vg0
1046 salt-call maasng.delete_volume_group server_hostname vg0
azvyagintsevbca1f462018-05-25 19:06:46 +03001047 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001048
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001049 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001050 system_id = get_machine(hostname)["system_id"]
azvyagintsevbca1f462018-05-25 19:06:46 +03001051 LOG.debug('delete_volume_group:{}'.format(system_id))
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001052
azvyagintsevbca1f462018-05-25 19:06:46 +03001053 vg_id = str(_get_volume_group_id_by_name(hostname, name))
1054 for vol in get_volumes(hostname, name):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001055 delete_volume(hostname, vol, name)
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001056
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001057 # TODO validation
1058 json_res = json.loads(maas.delete(
1059 u"api/2.0/nodes/{0}/volume-group/{1}/".format(system_id, vg_id)).read() or 'null')
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001060 LOG.info(json_res)
1061
1062 return True
1063
1064
azvyagintsevf3515c82018-06-26 18:59:05 +03001065def create_volume(hostname, volume_name, volume_group, size, fs_type=None,
1066 mount=None):
azvyagintsevbca1f462018-05-25 19:06:46 +03001067 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001068 Create volume on volume group.
1069
1070 CLI Example:
1071
1072 .. code-block:: bash
1073
1074 salt 'maas-node' maasng.create_volume server_hostname volume_name, volume_group, size, fs_type=None, mount=None
1075 salt-call maasng.create_volume server_hostname volume_name, volume_group, size, fs_type=None, mount=None
azvyagintsevbca1f462018-05-25 19:06:46 +03001076 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001077
1078 data = {
1079 "name": volume_name,
1080 }
1081
1082 value, unit = size[:-1], size[-1]
1083 bit_size = str(int(value) * SIZE[unit])
1084 LOG.info(bit_size)
1085
1086 data["size"] = bit_size
1087
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001088 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001089 system_id = get_machine(hostname)["system_id"]
1090 LOG.info(system_id)
1091
1092 volume_group_id = str(_get_volume_group_id_by_name(hostname, volume_group))
1093
1094 LOG.info(volume_group_id)
1095
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001096 # TODO validation
1097 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/volume-group/{1}/".format(
1098 system_id, volume_group_id), "create_logical_volume", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001099 LOG.info(json_res)
1100
1101 if fs_type != None or mount != None:
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001102 ret = create_volume_filesystem(
1103 hostname, volume_group + "-" + volume_name, fs_type, mount)
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001104
1105 return True
1106
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001107
azvyagintsevbca1f462018-05-25 19:06:46 +03001108def delete_volume(hostname, volume_name, volume_group):
1109 """
1110 Delete volume from volume group.
1111 Tips: maas always use 'volume_group-volume_name' name schema.Example: 'vg0-glusterfs'
1112 This function expexts same format.
1113
1114 CLI Example:
1115
1116 .. code-block:: bash
1117
1118 salt 'maas-node' maasng.delete_volume server_hostname volume_name volume_group
1119 salt 'maas-node' maasng.delete_volume server_hostname vg0-vol0 vg0
1120 salt-call maasng.delete_volume server_hostname volume_name volume_group
1121 """
1122
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001123 maas = _create_maas_client()
azvyagintsevbca1f462018-05-25 19:06:46 +03001124 system_id = get_machine(hostname)["system_id"]
1125 LOG.debug('delete_volume:{}'.format(system_id))
1126
1127 volume_group_id = str(_get_volume_group_id_by_name(hostname, volume_group))
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001128 volume_id = str(_get_volume_id_by_name(
1129 hostname, volume_name, volume_group))
azvyagintsevbca1f462018-05-25 19:06:46 +03001130
1131 if None in [volume_group_id, volume_id]:
1132 return False
1133
1134 data = {
1135 "id": volume_id,
1136 }
1137
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001138 # TODO validation
1139 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/volume-group/{1}/".format(
1140 system_id, volume_group_id), "delete_logical_volume", **data).read() or 'null')
azvyagintsevbca1f462018-05-25 19:06:46 +03001141 return True
1142
1143
1144def get_volumes(hostname, vg_name):
1145 """
1146 Get list of volumes in volume group.
1147 """
1148 volumes = {}
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001149 _volumes = list_volume_groups(
1150 hostname)[vg_name].get('logical_volumes', False)
azvyagintsevbca1f462018-05-25 19:06:46 +03001151 if _volumes:
1152 for item in _volumes:
1153 volumes[item["name"]] = item
1154 return volumes
1155
1156# END LVM
1157
1158
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001159def create_volume_filesystem(hostname, device, fs_type=None, mount=None):
1160
1161 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001162 system_id = get_machine(hostname)["system_id"]
1163
1164 blockdevices_id = _get_blockdevice_id_by_name(hostname, device)
1165 data = {}
1166 if fs_type != None:
1167 data["fstype"] = fs_type
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001168 # TODO validation
1169 json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
1170 system_id, blockdevices_id), "format", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001171 LOG.info(json_res)
1172
1173 if mount != None:
1174 data["mount_point"] = mount
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001175 # TODO validation
1176 json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
1177 system_id, blockdevices_id), "mount", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001178 LOG.info(json_res)
1179
1180 return True
1181
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001182
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001183def set_boot_disk(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +03001184 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001185 Create volume on volume group.
1186
1187 CLI Example:
1188
1189 .. code-block:: bash
1190
1191 salt 'maas-node' maasng.set_boot_disk server_hostname disk_name
1192 salt-call maasng.set_boot_disk server_hostname disk_name
azvyagintsevbca1f462018-05-25 19:06:46 +03001193 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001194 data = {}
1195 result = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001196 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001197 system_id = get_machine(hostname)["system_id"]
1198 blockdevices_id = _get_blockdevice_id_by_name(hostname, name)
1199
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001200 maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
1201 system_id, blockdevices_id), "set_boot_disk", **data).read()
azvyagintsevf3515c82018-06-26 18:59:05 +03001202 # TODO validation for error response
1203 # (disk does not exists and node does not exists)
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001204 result["new"] = "Disk {0} was set as bootable".format(name)
1205
1206 return result
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001207
azvyagintsevbca1f462018-05-25 19:06:46 +03001208# NETWORKING
1209
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001210
1211def list_fabric():
azvyagintsevbca1f462018-05-25 19:06:46 +03001212 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001213 Get list of all fabric
1214
1215 CLI Example:
1216
1217 .. code-block:: bash
1218
1219 salt 'maas-node' maasng.list_fabric
azvyagintsevbca1f462018-05-25 19:06:46 +03001220 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001221 fabrics = {}
1222 maas = _create_maas_client()
1223 json_res = json.loads(maas.get(u'api/2.0/fabrics/').read())
1224 LOG.info(json_res)
1225 for item in json_res:
1226 fabrics[item["name"]] = item
1227 return fabrics
1228
1229
azvyagintsevf3515c82018-06-26 18:59:05 +03001230def check_fabric(name):
1231 """
1232 Simple check that fabric already defined
1233 Return format:
1234 update - require update
1235 correct - fully coincides # not implemented
1236 not_exist - need's to be created
1237 """
1238
1239 ret = 'not_exist'
1240 fabrics = list_fabric()
1241 if name in fabrics.keys():
1242 LOG.debug("Requested fabrics with name:{} already exist".format(name))
1243 ret = 'update'
1244 return ret
1245
1246
1247def check_fabric_guess_with_cidr(name, cidrs):
1248 """
1249 Check, that fabric already defined OR it was autodiscovered
1250 WA to fix issue with hardcoded 'fabric-0'
1251 - Find all auto-discovered subnets by cidr
1252 - find all subnets, that SHOULD be configured to THIS subent
1253 Warning: most probably, will fail if some subnet defined
1254 to another fabric :(
1255
1256 { 'update' : ID } - require update
1257 { 'correct' : ID } - fully coincides # not implemented
1258 { 'not_exist' : None } - need's to be created
1259
1260 CLI Example:
1261
1262 .. code-block:: bash
1263
1264 salt 'maas-node' maasng.check_fabric_guess_with_cidr name='' cidrs=[]
1265 """
1266
1267 ret = {'not_exist': None}
1268 fabrics = list_fabric()
1269 # Simple check
1270 if name in fabrics:
1271 LOG.debug("Requested fabrics with name:{} already exist".format(name))
1272 f_id = fabrics[name]['id']
1273 ret = {'update': f_id}
1274 # Cidr check
1275 # All discovered subnets by cidr
1276 d_subnets = list_subnets(sort_by='cidr')
1277 # Check, that requested cidr already in discovered.
1278 # If it is - it would mean that fabric already
1279 # exist(fabric-0,most probably) but should be renamed.
1280 # Works only for first shot ;(
1281 # due curren-single-maas logic for 'vlan-subnet' mapping.
1282 # Probably, it will fail with future MAAS releases.
1283 for cidr in cidrs:
1284 if cidr in d_subnets:
1285 f_id = d_subnets[cidr]['vlan']['fabric_id']
1286 f_name = d_subnets[cidr]['vlan']['fabric']
1287 LOG.warning("Detected cidr:{} in fabric:{}".format(cidr, f_name))
1288 LOG.warning("Guessing, that fabric "
1289 "with current name:{}\n should be "
1290 "renamed to:{}".format(f_name, name))
1291 ret = {'update': f_id}
1292 return ret
1293 return ret
1294
1295
azvyagintsevf0904ac2018-07-05 18:53:26 +03001296def create_fabric(name, description=None, fabric_id=None, update=False):
azvyagintsevbca1f462018-05-25 19:06:46 +03001297 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001298 Create new fabric.
1299
1300 CLI Example:
1301
1302 .. code-block:: bash
1303
azvyagintsevf3515c82018-06-26 18:59:05 +03001304 salt 'maas-node' maasng.create_fabric name='123'
azvyagintsevbca1f462018-05-25 19:06:46 +03001305 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001306 result = {}
1307 data = {
1308 "name": name,
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001309 "class_type": '',
1310
1311 }
azvyagintsevf3515c82018-06-26 18:59:05 +03001312 if description:
1313 data['description'] = description
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001314
1315 maas = _create_maas_client()
azvyagintsevf0904ac2018-07-05 18:53:26 +03001316 json_res = None
azvyagintsevf3515c82018-06-26 18:59:05 +03001317 try:
1318 if update:
1319 json_res = json.loads(
1320 maas.put(u"api/2.0/fabrics/{0}/".format(fabric_id),
1321 **data).read())
1322 result["new"] = "Fabric {0} created".format(json_res["name"])
1323 else:
1324 json_res = json.loads(
1325 maas.post(u"api/2.0/fabrics/", None, **data).read())
1326 result["changes"] = "Fabric {0} updated".format(json_res["name"])
1327 except Exception as inst:
1328 LOG.debug("create_fabric data:{}".format(data))
azvyagintsev06b74932019-04-03 15:27:31 +03001329 m = inst.message
azvyagintsevf3515c82018-06-26 18:59:05 +03001330 LOG.error("Message:{0}".format(m))
1331 result['result'] = False
1332 result['comment'] = 'Error creating fabric: {0}'.format(name)
1333 result['error'] = m
1334 return result
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001335 LOG.debug("crete_fabric:{}".format(json_res))
azvyagintsevf3515c82018-06-26 18:59:05 +03001336 result['result'] = True
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001337 return result
1338
1339
azvyagintsevf3515c82018-06-26 18:59:05 +03001340def list_subnets(sort_by='name'):
azvyagintsevbca1f462018-05-25 19:06:46 +03001341 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001342 Get list of subnets from maas server
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001343
1344 CLI Example:
1345
1346 .. code-block:: bash
1347
azvyagintsevf3515c82018-06-26 18:59:05 +03001348 salt 'maas-node' maasng.list_subnets
azvyagintsevbca1f462018-05-25 19:06:46 +03001349 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001350 subnets = {}
1351 maas = _create_maas_client()
1352 json_res = json.loads(maas.get(u'api/2.0/subnets/').read())
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001353 for item in json_res:
azvyagintsevf3515c82018-06-26 18:59:05 +03001354 subnets[item[sort_by]] = item
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001355 return subnets
1356
1357
azvyagintsevf3515c82018-06-26 18:59:05 +03001358def list_vlans(fabric, sort_by='vid'):
azvyagintsevbca1f462018-05-25 19:06:46 +03001359 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001360 Get list of vlans in fabric
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001361
1362 CLI Example:
1363
1364 .. code-block:: bash
1365
azvyagintsevf3515c82018-06-26 18:59:05 +03001366 salt 'maas-node' maasng.list_vlans fabric_name
azvyagintsevbca1f462018-05-25 19:06:46 +03001367 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001368 vlans = {}
1369 maas = _create_maas_client()
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001370 fabric_id = get_fabricid(fabric)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001371
azvyagintsevf3515c82018-06-26 18:59:05 +03001372 try:
1373 json_res = json.loads(
1374 maas.get(u'api/2.0/fabrics/{0}/vlans/'.format(fabric_id)).read())
1375 except Exception as inst:
azvyagintsev06b74932019-04-03 15:27:31 +03001376 m = inst.message
azvyagintsevf3515c82018-06-26 18:59:05 +03001377 LOG.error("Message:{0}".format(m))
1378 LOG.debug(json_res)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001379 for item in json_res:
azvyagintsevf3515c82018-06-26 18:59:05 +03001380 vlans[item[sort_by]] = item
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001381 return vlans
1382
1383
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001384def get_fabricid(fabric):
azvyagintsevbca1f462018-05-25 19:06:46 +03001385 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001386 Get id for specific fabric
1387
1388 CLI Example:
1389
1390 .. code-block:: bash
1391
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001392 salt 'maas-node' maasng.get_fabricid fabric_name
azvyagintsevbca1f462018-05-25 19:06:46 +03001393 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001394 try:
1395 return list_fabric()[fabric]['id']
1396 except KeyError:
azvyagintsevefb6f5d2018-07-10 14:16:19 +03001397 return {"error": "Fabric not found on MaaS server"}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001398
1399
azvyagintsevf3515c82018-06-26 18:59:05 +03001400def check_vlan_in_fabric(fabric, vlan):
1401 """
1402 Check that VLAN exactly defined
1403 Return format:
1404 update - require update
1405 correct - fully coincides # not implemented
1406 not_exist - need's to be created
1407 """
1408
1409 ret = 'not_exist'
1410 vlans = list_vlans(fabric)
1411 if vlan in vlans.keys():
1412 LOG.debug("Requested VLAN:{} already exist"
1413 "in FABRIC:{}".format(vlan, fabric))
1414 ret = 'update'
1415 return ret
1416
1417
Petr Ruzicka80471852018-07-13 14:08:27 +02001418def create_vlan_in_fabric(name, fabric, vlan, description, primary_rack, mtu=1500,
Ivan Berezovskiy499b2502019-10-07 16:31:32 +04001419 dhcp_on=False, update=False, vlan_id="", relay_vlan=None):
azvyagintsevbca1f462018-05-25 19:06:46 +03001420 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001421 Update vlan
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001422 CLI Example:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001423 .. code-block:: bash
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001424 salt 'maas-node' maasng.update_vlan name, fabric, vid, description, dhcp_on
azvyagintsevbca1f462018-05-25 19:06:46 +03001425 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001426 result = {}
1427
1428 data = {
1429 "name": name,
1430 "dhcp_on": str(dhcp_on),
1431 "description": description,
azvyagintsev6913e5e2018-07-05 11:42:53 +03001432 "primary_rack": list_racks()[primary_rack]['system_id'],
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001433 }
Michael Polenchukd25da792018-07-19 18:27:11 +04001434 if mtu:
1435 data['mtu'] = str(mtu)
Ivan Berezovskiy499b2502019-10-07 16:31:32 +04001436 if relay_vlan:
1437 data['relay_vlan'] = str(relay_vlan)
azvyagintsevf3515c82018-06-26 18:59:05 +03001438 vlan = str(vlan)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001439 maas = _create_maas_client()
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001440 fabric_id = get_fabricid(fabric)
azvyagintsevf3515c82018-06-26 18:59:05 +03001441 try:
1442 if update:
1443 # MAAS have buggy logic here. Fallowing api reference, here should
1444 # be passed VID - which mean, API ID for vlan.
1445 # Otherwise, at least for maas 2.3.3-6498-ge4db91d exactly VLAN
1446 # should be passed. so, make temp.backward-convertation.
1447 # json_res = json.loads(maas.put(u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id,vlan_id), **data).read())
1448 json_res = json.loads(maas.put(
1449 u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id, vlan),
1450 **data).read())
1451 else:
1452 data['vid'] = str(vlan)
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001453 json_res = json.loads(maas.post(
1454 u'api/2.0/fabrics/{0}/vlans/'.format(fabric_id), None, **data).read())
azvyagintsevf3515c82018-06-26 18:59:05 +03001455 except Exception as inst:
1456 LOG.debug("create_vlan_in_fabric data:{}".format(data))
azvyagintsev06b74932019-04-03 15:27:31 +03001457 m = inst.message
azvyagintsevf3515c82018-06-26 18:59:05 +03001458 LOG.error("Message:{0}".format(m))
1459 result['result'] = False
1460 result['comment'] = 'Error updating vlan: {0}'.format(name)
1461 result['error'] = m
1462 return result
1463 LOG.debug("create_vlan_in_fabric:{}".format(json_res))
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001464 result["new"] = "Vlan {0} was updated".format(json_res["name"])
1465
1466 return result
azvyagintsevbca1f462018-05-25 19:06:46 +03001467
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001468
azvyagintsevf3515c82018-06-26 18:59:05 +03001469def check_subnet(cidr, name, fabric, gateway_ip):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001470 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001471 Check that subnet exactly defined
1472 Return format:
1473 update - require update
1474 correct - fully coincides # not implemented
1475 not_exist - need's to be created
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001476 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001477
1478 ret = 'not_exist'
1479 subnets = list_subnets(sort_by='cidr')
1480 if cidr in subnets.keys():
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001481 LOG.debug("Requested subnet cidr:{} already exist".format(cidr))
1482 ret = 'update'
azvyagintsevf3515c82018-06-26 18:59:05 +03001483 return ret
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001484
1485
azvyagintsevf3515c82018-06-26 18:59:05 +03001486def create_subnet(cidr='', name='', fabric='', gateway_ip='', vlan='',
1487 update=False, subnet_id=''):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001488 """
1489 Create subnet
1490
1491 CLI Example:
1492
1493 .. code-block:: bash
1494
1495 salt 'maas-node' maasng.create_subnet cidr, name, fabric, gateway_ip
1496 """
1497
1498 fabric_id = get_fabricid(fabric)
1499 result = {}
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001500 vlan = str(vlan)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001501 data = {
1502 "cidr": cidr,
1503 "name": name,
1504 "fabric": str(fabric_id),
1505 "gateway_ip": gateway_ip,
azvyagintsevf3515c82018-06-26 18:59:05 +03001506 'vlan': vlan,
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001507 }
1508 maas = _create_maas_client()
azvyagintsevf3515c82018-06-26 18:59:05 +03001509 # FIXME: vlan definition not work in 2.3.3-6498-ge4db91d.
1510 LOG.warning("Ignoring parameter vlan:{}".format(vlan))
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001511 data.pop('vlan', '')
azvyagintsevf3515c82018-06-26 18:59:05 +03001512 try:
1513 if update:
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001514 json_res = json.loads(
1515 maas.put(u"api/2.0/subnets/{0}/".format(subnet_id), **data).read())
azvyagintsevf3515c82018-06-26 18:59:05 +03001516 else:
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001517 json_res = json.loads(
1518 maas.post(u"api/2.0/subnets/", None, **data).read())
azvyagintsevf3515c82018-06-26 18:59:05 +03001519 except Exception as inst:
1520 LOG.debug("create_subnet data:{}".format(data))
azvyagintsev06b74932019-04-03 15:27:31 +03001521 m = inst.message
azvyagintsevf3515c82018-06-26 18:59:05 +03001522 LOG.error("Message:{0}".format(m))
1523 result['result'] = False
1524 result['comment'] = 'Error creating subnet: {0}'.format(name)
1525 result['error'] = m
1526 return result
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001527 LOG.debug("create_subnet:{}".format(json_res))
azvyagintsevf3515c82018-06-26 18:59:05 +03001528 result["new"] = "Subnet {0} with CIDR {1}" \
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001529 "and gateway {2} was created".format(
1530 name, cidr, gateway_ip)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001531
1532 return result
1533
1534
1535def get_subnet(subnet):
1536 """
1537 Get details for specific subnet
1538
1539 CLI Example:
1540
1541 .. code-block:: bash
1542
1543 salt 'maas-node' maasng.get_subnet subnet_name
1544 """
1545 try:
azvyagintsevf3515c82018-06-26 18:59:05 +03001546 return list_subnets()[subnet]
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001547 except KeyError:
1548 return {"error": "Subnet not found on MaaS server"}
1549
1550
1551def get_subnetid(subnet):
1552 """
1553 Get id for specific subnet
1554
1555 CLI Example:
1556
1557 .. code-block:: bash
1558
1559 salt 'maas-node' maasng.get_subnetid subnet_name
1560 """
1561 try:
azvyagintsevf3515c82018-06-26 18:59:05 +03001562 return list_subnets()[subnet]['id']
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001563 except KeyError:
1564 return {"error": "Subnet not found on MaaS server"}
1565
1566
1567def list_ipranges():
1568 """
1569 Get list of all ipranges from maas server
1570
1571 CLI Example:
1572
1573 .. code-block:: bash
1574
1575 salt 'maas-node' maasng.list_ipranges
1576 """
1577 ipranges = {}
1578 maas = _create_maas_client()
1579 json_res = json.loads(maas.get(u'api/2.0/ipranges/').read())
1580 for item in json_res:
1581 ipranges[item["start_ip"]] = item
1582 return ipranges
1583
1584
azvyagintsevf0904ac2018-07-05 18:53:26 +03001585def create_iprange(type_range, start_ip, end_ip, subnet=None, comment=None):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001586 """
1587 Create ip range
1588
1589 CLI Example:
1590
1591 .. code-block:: bash
1592
1593 salt 'maas-node' maasng.create_iprange type, start ip, end ip, comment
1594 """
1595 result = {}
1596
1597 data = {
1598 "type": type_range,
1599 "start_ip": start_ip,
1600 "end_ip": end_ip,
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001601 }
azvyagintsevf3515c82018-06-26 18:59:05 +03001602 if comment:
1603 data['comment'] = comment
azvyagintsevf0904ac2018-07-05 18:53:26 +03001604 if subnet:
1605 subnet_id = list_subnets()[subnet]['id']
1606 data['subnet'] = str(subnet_id)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001607 maas = _create_maas_client()
azvyagintsevf3515c82018-06-26 18:59:05 +03001608 _name = "Type:{}: {}-{}".format(type_range, start_ip, end_ip)
1609 try:
1610 json_res = json.loads(
1611 maas.post(u"api/2.0/ipranges/", None, **data).read())
1612 except Exception as inst:
azvyagintsev06b74932019-04-03 15:27:31 +03001613 m = inst.message
azvyagintsevf3515c82018-06-26 18:59:05 +03001614 LOG.error("Message:{0}".format(m))
1615 result['result'] = False
1616 result['comment'] = 'Error creating iprange:{0}'.format(_name)
1617 result['error'] = m
1618 return result
1619 result["new"] = "Iprange: {0} has been created".format(_name)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001620 LOG.debug("create_iprange:{}".format(json_res))
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001621
1622 return result
1623
1624
1625def get_iprangeid(start_ip):
1626 """
1627 Get id for ip range from maas server
1628
1629 CLI Example:
1630
1631 .. code-block:: bash
1632
1633 salt 'maas-node' maasng.get_iprangeid start_ip
1634 """
1635 try:
1636 return list_ipranges()[start_ip]['id']
1637 except KeyError:
1638 return {"error": "Ip range not found on MaaS server"}
1639
1640
1641def get_startip(start_ip):
1642 """
1643 Get start ip for ip range
1644
1645 CLI Example:
1646
1647 .. code-block:: bash
1648
1649 salt 'maas-node' maasng.get_startip start ip
1650 """
1651 try:
1652 return list_ipranges()[start_ip]
1653 except KeyError:
1654 return {"error": "Ip range not found on MaaS server"}
azvyagintsevbca1f462018-05-25 19:06:46 +03001655# END NETWORKING
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001656
1657# MAAS CONFIG SECTION
1658
1659
azvyagintsev58947072018-06-29 12:09:48 +03001660def _getHTTPCode(url):
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001661 code = '003'
1662 m = ''
azvyagintsev58947072018-06-29 12:09:48 +03001663 try:
1664 connection = urllib2.urlopen(url)
1665 code = connection.getcode()
1666 connection.close()
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001667 except (urllib2.HTTPError, urllib2.URLError) as e:
1668 try:
1669 code = e.getcode()
1670 except:
1671 m = e.reason
1672 pass
azvyagintsevf3515c82018-06-26 18:59:05 +03001673 LOG.debug("Unexpected http code:{} from "
1674 "url:{}\nwith message:{}".format(code, url, m))
azvyagintsev58947072018-06-29 12:09:48 +03001675 pass
1676 return code
1677
1678
1679def wait_for_http_code(url=None, expected=[200]):
1680 """
1681 Simple function, which just wait for avaible api, aka wait for 200.
1682
1683 CLI Example:
1684
1685 .. code-block:: bash
1686
1687 salt 'maas-node' maasng.wait_for_http_code url expected=[200]
1688
1689 """
1690 ret = {}
1691 started_at = time.time()
1692 poll_time = 5
1693 timeout = 60 * 2
1694 while _getHTTPCode(url) not in expected:
1695 c_timeout = timeout - (time.time() - started_at)
1696 if c_timeout <= 0:
1697 ret['result'] = False
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001698 ret["comment"] = "api:{} not answered in time".format(url)
azvyagintsev58947072018-06-29 12:09:48 +03001699 return ret
1700 LOG.info(
1701 "Waiting for api:{0}\n"
1702 "sleep for:{1}s "
1703 "Left:{2}/{3}s".format(url, poll_time, round(c_timeout),
1704 timeout))
1705 time.sleep(poll_time)
1706 ret['result'] = True
1707 ret["comment"] = "MAAS API:{} up.".format(url)
1708 return ret
1709
1710
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001711def _get_boot_source_id_by_url(url):
1712 # FIXME: fix ret\validation
1713 try:
1714 bs_id = get_boot_source(url=url)["id"]
1715 except KeyError:
1716 return {"error": "boot-source:{0} not exist!".format(url)}
1717 return bs_id
1718
1719
1720def get_boot_source(url=None):
1721 """
1722 Read a boot source by url. If url not specified - return all.
1723
1724 CLI Example:
1725
1726 .. code-block:: bash
1727
1728 salt 'maas-node' maasng.get_boot_source url
1729
1730 """
1731 boot_sources = {}
1732 maas = _create_maas_client()
1733 json_res = json.loads(maas.get(u'api/2.0/boot-sources/').read() or 'null')
1734 for item in json_res:
1735 boot_sources[str(item["url"])] = item
1736 if url:
1737 return boot_sources.get(url, {})
1738 return boot_sources
1739
1740
1741def delete_boot_source(url, bs_id=None):
1742 """
1743 Delete a boot source by url.
1744
1745 CLI Example:
1746
1747 .. code-block:: bash
1748
1749 sal 'maas-node' maasng.delete url
1750
1751 """
1752 result = {}
1753 if not bs_id:
1754 bs_id = _get_boot_source_id_by_url(url)
1755 maas = _create_maas_client()
1756 json_res = json.loads(maas.delete(
1757 u'/api/2.0/boot-sources/{0}/'.format(bs_id)).read() or 'null')
1758 LOG.debug("delete_boot_source:{}".format(json_res))
1759 result["new"] = "Boot-resource {0} deleted".format(url)
1760 return result
1761
1762
1763def boot_sources_delete_all_others(except_urls=[]):
1764 """
1765 Delete all boot-sources, except defined in 'except_urls' list.
1766 """
azvyagintseve2e37a12018-11-01 14:45:49 +02001767 result = {'changes': {}}
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001768 maas_boot_sources = get_boot_source()
1769 if 0 in [len(except_urls), len(maas_boot_sources)]:
1770 result['result'] = None
azvyagintseve2e37a12018-11-01 14:45:49 +02001771 result["comment"] = "'except_urls' or 'maas boot-sources' for " \
1772 "delete empty. No changes goinng to be."
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001773 return result
azvyagintseve2e37a12018-11-01 14:45:49 +02001774 # FIXME: fix 'changes' accumulator
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001775 for url in maas_boot_sources.keys():
1776 if url not in except_urls:
1777 LOG.info("Removing boot-source:{}".format(url))
1778 boot_resources_import(action='stop_import', wait=True)
1779 result["changes"] = delete_boot_source(url)
1780 return result
1781
1782
1783def create_boot_source(url, keyring_filename='', keyring_data='', wait=False):
1784 """
1785 Create and import maas boot-source: link to maas-ephemeral repo
1786 Be aware, those step will import resource to rack ctrl, but you also need to import
1787 them into the region!
1788
1789
1790 :param url: The URL of the BootSource.
1791 :param keyring_filename: The path to the keyring file for this BootSource.
1792 :param keyring_data: The GPG keyring for this BootSource, base64-encoded data.
1793
1794 """
1795
1796 # TODO: not work with 'update' currently => keyring update may fail.
1797 result = {}
1798
1799 data = {
1800 "url": url,
1801 "keyring_filename": keyring_filename,
1802 "keyring_data": str(keyring_data),
1803 }
1804
1805 maas = _create_maas_client()
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001806 if url in get_boot_source():
1807 result['result'] = None
1808 result["comment"] = "boot resource already exist"
1809 return result
1810
1811 # NOTE: maas.post will return 400, if url already defined.
1812 json_res = json.loads(
1813 maas.post(u'api/2.0/boot-sources/', None, **data).read())
1814 if wait:
1815 LOG.debug(
1816 "Sleep for 5s,to get MaaS some time to process previous request")
1817 time.sleep(5)
1818 ret = boot_resources_is_importing(wait=True)
1819 if ret is dict:
1820 return ret
1821 LOG.debug("create_boot_source:{}".format(json_res))
1822 result["new"] = "boot resource {0} was created".format(json_res["url"])
1823
1824 return result
1825
1826
1827def boot_resources_import(action='import', wait=False):
1828 """
1829 import/stop_import the boot resources.
1830
1831 :param action: import\stop_import
1832 :param wait: True\False. Wait till process finished.
1833
1834 CLI Example:
1835
1836 .. code-block:: bash
1837
1838 salt 'maas-node' maasng.boot_resources_import action='import'
1839
1840 """
1841 maas = _create_maas_client()
1842 # Have no idea why, but usual jsonloads not work here..
1843 imp = maas.post(u'api/2.0/boot-resources/', action)
1844 if imp.code == 200:
1845 LOG.debug('boot_resources_import:{}'.format(imp.readline()))
1846 if wait:
1847 boot_resources_is_importing(wait=True)
1848 return True
1849 else:
1850 return False
1851
1852
1853def boot_resources_is_importing(wait=False):
1854 maas = _create_maas_client()
1855 result = {}
1856 if wait:
1857 started_at = time.time()
1858 poll_time = 5
1859 timeout = 60 * 15
1860 while boot_resources_is_importing(wait=False):
1861 c_timeout = timeout - (time.time() - started_at)
1862 if c_timeout <= 0:
1863 result['result'] = False
1864 result["comment"] = "Boot-resources import not finished in time"
1865 return result
1866 LOG.info(
1867 "Waiting boot-resources import done\n"
1868 "sleep for:{}s "
1869 "Left:{}/{}s".format(poll_time, round(c_timeout), timeout))
1870 time.sleep(poll_time)
1871 return json.loads(
1872 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1873 else:
1874 return json.loads(
1875 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1876
1877#####
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001878# def boot_sources_selections_delete_all_others(except_urls=[]):
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001879# """
1880# """
1881# result = {}
1882# return result
1883
1884
1885def is_boot_source_selections_in(dict1, list1):
1886 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001887 Check that requested boot-selection already in maas bs selections,
1888 if True- return bss id.
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001889 # FIXME: those hack check doesn't look good.
1890 """
1891 for bs in list1:
1892 same = set(dict1.keys()) & set(bs.keys())
1893 if all(elem in same for elem in
1894 ['os', 'release', 'arches', 'subarches', 'labels']):
azvyagintsevf3515c82018-06-26 18:59:05 +03001895 LOG.debug("boot-selection in maas:{0}\n"
1896 "looks same to requested:{1}".format(bs, dict1))
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001897 return bs['id']
1898 return False
1899
1900
1901def get_boot_source_selections(bs_url):
1902 """
1903 Get boot-source selections.
1904 """
1905 # check for key_error!
1906 bs_id = _get_boot_source_id_by_url(bs_url)
1907 maas = _create_maas_client()
1908 json_res = json.loads(
1909 maas.get(u'/api/2.0/boot-sources/{0}/selections/'.format(bs_id)).read())
1910 LOG.debug(
1911 "get_boot_source_selections for url:{} \n{}".format(bs_url, json_res))
1912 return json_res
1913
1914
1915def create_boot_source_selections(bs_url, os, release, arches="*",
1916 subarches="*", labels="*", wait=True):
1917 """
1918 Create a new boot source selection for bs_url.
1919 :param os: The OS (e.g. ubuntu, centos) for which to import resources.Required.
1920 :param release: The release for which to import resources. Required.
1921 :param arches: The architecture list for which to import resources.
1922 :param subarches: The subarchitecture list for which to import resources.
1923 :param labels: The label lists for which to import resources.
1924 """
1925
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001926 result = {"result": True, 'name': bs_url, 'changes': None}
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001927
1928 data = {
1929 "os": os,
1930 "release": release,
1931 "arches": arches,
1932 "subarches": subarches,
1933 "labels": labels,
1934 }
1935
1936 maas = _create_maas_client()
1937 bs_id = _get_boot_source_id_by_url(bs_url)
1938 # TODO add pre-create verify
1939 maas_bs_s = get_boot_source_selections(bs_url)
1940 if is_boot_source_selections_in(data, maas_bs_s):
1941 result["result"] = True
azvyagintsevf3515c82018-06-26 18:59:05 +03001942 result["comment"] = 'Requested boot-source selection ' \
1943 'for {0} already exist.'.format(
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001944 bs_url)
1945 return result
1946
1947 # NOTE: maas.post will return 400, if url already defined.
azvyagintsevcb54d142018-06-19 16:18:32 +03001948 # Also, maas need's some time to import info about stream.
1949 # unfortunatly, maas don't have any call to check stream-import-info - so, we need to implement
1950 # at least simple retry ;(
1951 json_res = False
1952 poll_time = 5
azvyagintseve2e37a12018-11-01 14:45:49 +02001953 for i in range(0, 10):
azvyagintsevcb54d142018-06-19 16:18:32 +03001954 try:
1955 json_res = json.loads(
1956 maas.post(u'api/2.0/boot-sources/{0}/selections/'.format(bs_id), None,
1957 **data).read())
1958 except Exception as inst:
azvyagintsev06b74932019-04-03 15:27:31 +03001959 m = inst.message
azvyagintsevf3515c82018-06-26 18:59:05 +03001960 LOG.warning("boot_source_selections "
1961 "catch error during processing. Most-probably, "
azvyagintseve2e37a12018-11-01 14:45:49 +02001962 "streams data not imported yet.\nSleep:{}s "
1963 "Retry:{}/10".format(poll_time, i))
azvyagintsevcb54d142018-06-19 16:18:32 +03001964 LOG.warning("Message:{0}".format(m))
1965 time.sleep(poll_time)
1966 continue
1967 break
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001968 LOG.debug("create_boot_source_selections:{}".format(json_res))
azvyagintsevcb54d142018-06-19 16:18:32 +03001969 if not json_res:
1970 result["result"] = False
azvyagintsevf3515c82018-06-26 18:59:05 +03001971 result["comment"] = 'Failed to create requested boot-source selection' \
1972 ' for {0}.'.format(bs_url)
azvyagintsevcb54d142018-06-19 16:18:32 +03001973 return result
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001974 if wait:
1975 LOG.debug(
1976 "Sleep for 5s,to get MaaS some time to process previous request")
1977 time.sleep(5)
1978 ret = boot_resources_import(action='import', wait=True)
1979 if ret is dict:
1980 return ret
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001981 result["comment"] = "boot-source selection for {0} was created".format(
1982 bs_url)
azvyagintsevcb54d142018-06-19 16:18:32 +03001983 result["new"] = data
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001984
1985 return result
1986
1987# END MAAS CONFIG SECTION
azvyagintsevcb54d142018-06-19 16:18:32 +03001988
1989# RACK CONTROLLERS SECTION
1990
1991
1992def get_rack(hostname):
1993 """
1994 Get information about specified rackd
1995
1996 CLI Example:
1997
1998 .. code-block:: bash
1999
2000 salt-call maasng.get_rack rack_hostname
2001 """
2002 try:
2003 return list_racks()[hostname]
2004 except KeyError:
2005 return {"error": "rack:{} not found on MaaS server".format(hostname)}
2006
2007
azvyagintsev6913e5e2018-07-05 11:42:53 +03002008def list_racks(sort_by='hostname'):
azvyagintsevcb54d142018-06-19 16:18:32 +03002009 """
2010 Get list of all rack controllers from maas server
2011
2012 CLI Example:
2013
2014 .. code-block:: bash
2015
2016 salt-call maasng.list_racks
2017 """
2018 racks = {}
2019 maas = _create_maas_client()
2020 json_res = json.loads(
2021 maas.get(u"/api/2.0/rackcontrollers/").read() or 'null')
2022 for item in json_res:
azvyagintsev6913e5e2018-07-05 11:42:53 +03002023 racks[item[sort_by]] = item
azvyagintsevcb54d142018-06-19 16:18:32 +03002024 return racks
2025
2026
2027def sync_bs_to_rack(hostname=None):
2028 """
2029 Sync RACK boot-sources with REGION. If no hostname probided - sync to all.
2030
2031 CLI Example:
2032
2033 .. code-block:: bash
2034
2035 salt-call maasng.sync_bs_to_rack rack_hostname
2036 """
2037 ret = {}
2038 maas = _create_maas_client()
2039 if not hostname:
2040 LOG.info("boot-sources sync initiated for ALL Rack's")
2041 # Convert to json-like format
2042 json_res = json.loads('["{0}"]'.format(
2043 maas.post(u"/api/2.0/rackcontrollers/",
2044 'import_boot_images').read()))
2045 LOG.debug("sync_bs_to_rack:{}".format(json_res))
2046 ret['result'] = True
2047 ret['comment'] = "boot-sources sync initiated for ALL Rack's"
2048 return ret
2049 LOG.info("boot-sources sync initiated for RACK:{0}".format(hostname))
2050 # Convert to json-like format
2051 json_res = json.loads('["{0}"]'.format(maas.post(
2052 u"/api/2.0/rackcontrollers/{0}/".format(
2053 get_rack(hostname)['system_id']),
2054 'import_boot_images').read()))
2055 LOG.debug("sync_bs_to_rack:{}".format(json_res))
2056 ret['result'] = True
2057 ret['comment'] = "boot-sources sync initiated for {0} Rack's".format(
2058 hostname)
2059 return
2060
2061
2062def rack_list_boot_imgs(hostname):
2063 ret = {}
2064 maas = _create_maas_client()
2065 LOG.debug("rack_list_boot_imgs:{}".format(hostname))
2066 ret = json.loads(maas.get(u"/api/2.0/rackcontrollers/{0}/".format(
2067 get_rack(hostname)['system_id']), 'list_boot_images').read() or 'null')
2068 return ret
2069
2070
2071def is_rack_synced(hostname):
2072 rez = rack_list_boot_imgs(hostname)['status']
2073 if rez == 'synced':
2074 return True
2075 return False
2076
2077# TODO do we actually need _exact_ check per-pack?
2078# def wait_for_images_on_rack(hostname):
2079#
2080# """
2081# WA function, to be able check that RACK actually done SYNC images
2082# for REQUIRED images at least.
2083# Required image to be fetched from
2084# reclass:maas:region:boot_sources_selections:[keys]:os/release formation
2085#
2086# CLI Example:
2087#
2088# .. code-block:: bash
2089#
2090# salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
2091# """
2092# try:
2093# bss = __salt__['config.get']('maas')['region']['boot_sources_selections']
2094# except KeyError:
2095# ret['result'] = None
2096# ret['comment'] = "boot_sources_selections definition for sync not found."
2097# return ret
2098# s_names = []
2099# # Format u'name': u'ubuntu/xenial'
2100# for v in bss.values():s_names.append("{0}/{1}".format(v['os'],v['release']))
2101# # Each names, should be in rack and whole rack should be in sync-ed state
2102
2103
2104def sync_and_wait_bs_to_all_racks():
2105 """
2106 Sync ALL rack's with regions source images.
2107
2108 CLI Example:
2109
2110 .. code-block:: bash
2111
2112 salt-call maasng.sync_and_wait_bs_to_all_racks
2113 """
2114 sync_bs_to_rack()
2115 for rack in list_racks().keys():
2116 wait_for_sync_bs_to_rack(hostname=rack)
2117 return True
2118
2119
2120def wait_for_sync_bs_to_rack(hostname=None):
2121 """
2122 Wait for boot images sync finished, on exact rack
2123
2124 CLI Example:
2125
2126 .. code-block:: bash
2127
2128 salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
2129 """
2130 ret = {}
2131 started_at = time.time()
2132 poll_time = 5
2133 timeout = 60 * 15
2134 while not is_rack_synced(hostname):
2135 c_timeout = timeout - (time.time() - started_at)
2136 if c_timeout <= 0:
2137 ret['result'] = False
2138 ret[
2139 "comment"] = "Boot-resources sync on rackd:{0}" \
2140 "not finished in time".format(
2141 hostname)
2142 return ret
2143 LOG.info(
2144 "Waiting boot-resources sync done to rack:{0}\n"
2145 "sleep for:{1}s "
2146 "Left:{2}/{3}s".format(hostname, poll_time, round(c_timeout),
2147 timeout))
2148 time.sleep(poll_time)
2149 ret['result'] = is_rack_synced(hostname)
2150 ret["comment"] = "Boot-resources sync on rackd:{0} finished".format(
2151 hostname)
2152 return ret
2153
2154# END RACK CONTROLLERS SECTION
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02002155# SSHKEYS
2156
2157
2158def list_sshkeys():
2159 """
2160 Get list of all sshkeys
2161
2162 CLI Example:
2163
2164 .. code-block:: bash
2165
2166 salt 'maas-node' maasng.list_sshkeys
2167 salt-call maasng.list_sshkeys
2168 """
2169 ssh = {}
2170 maas = _create_maas_client()
2171 json_res = json.loads(maas.get(u'api/2.0/account/prefs/sshkeys/').read())
2172 LOG.info(json_res)
2173 for item in json_res:
2174 ssh[item["key"]] = item
2175 return ssh
2176
2177
2178def add_sshkey(sshkey):
2179 """
2180 Add SSH key for user to MAAS.
2181
2182 CLI Example:
2183
2184 .. code-block:: bash
2185
2186 salt 'maas-node' maasng.add_sshkey sshkey
2187 salt-call maasng.add_sshkey sshkey
2188 """
2189 data = {
2190 "key": sshkey,
2191 }
2192 result = {}
2193 maas = _create_maas_client()
2194
2195 maas.post(u"/api/2.0/account/prefs/sshkeys/", None, **data).read()
2196 result["new"] = "SSH Key {0} was added.".format(sshkey)
2197
2198 return result
2199
2200
2201def get_sshkey(sshkey):
2202 """
2203 Get start ip for ip range
2204
2205 CLI Example:
2206
2207 .. code-block:: bash
2208
2209 salt 'maas-node' maasng.get_sshkey sshkey
2210 salt-call maasng.get_sshkey sshkey
2211 """
2212 try:
2213 return list_sshkeys()[sshkey]
2214 except KeyError:
2215 return {"error": "SSH key not found on MaaS server"}
2216# END SSHKEYS