blob: 72f22b72e55a7f94b131a7359dec1289b2be5405 [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))
1329 try:
1330 m = inst.readlines()
1331 except:
1332 m = inst.message
1333 LOG.error("Message:{0}".format(m))
1334 result['result'] = False
1335 result['comment'] = 'Error creating fabric: {0}'.format(name)
1336 result['error'] = m
1337 return result
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001338 LOG.debug("crete_fabric:{}".format(json_res))
azvyagintsevf3515c82018-06-26 18:59:05 +03001339 result['result'] = True
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001340 return result
1341
1342
azvyagintsevf3515c82018-06-26 18:59:05 +03001343def list_subnets(sort_by='name'):
azvyagintsevbca1f462018-05-25 19:06:46 +03001344 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001345 Get list of subnets from maas server
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001346
1347 CLI Example:
1348
1349 .. code-block:: bash
1350
azvyagintsevf3515c82018-06-26 18:59:05 +03001351 salt 'maas-node' maasng.list_subnets
azvyagintsevbca1f462018-05-25 19:06:46 +03001352 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001353 subnets = {}
1354 maas = _create_maas_client()
1355 json_res = json.loads(maas.get(u'api/2.0/subnets/').read())
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001356 for item in json_res:
azvyagintsevf3515c82018-06-26 18:59:05 +03001357 subnets[item[sort_by]] = item
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001358 return subnets
1359
1360
azvyagintsevf3515c82018-06-26 18:59:05 +03001361def list_vlans(fabric, sort_by='vid'):
azvyagintsevbca1f462018-05-25 19:06:46 +03001362 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001363 Get list of vlans in fabric
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001364
1365 CLI Example:
1366
1367 .. code-block:: bash
1368
azvyagintsevf3515c82018-06-26 18:59:05 +03001369 salt 'maas-node' maasng.list_vlans fabric_name
azvyagintsevbca1f462018-05-25 19:06:46 +03001370 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001371 vlans = {}
1372 maas = _create_maas_client()
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001373 fabric_id = get_fabricid(fabric)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001374
azvyagintsevf3515c82018-06-26 18:59:05 +03001375 try:
1376 json_res = json.loads(
1377 maas.get(u'api/2.0/fabrics/{0}/vlans/'.format(fabric_id)).read())
1378 except Exception as inst:
1379 m = inst.readlines()
1380 LOG.error("Message:{0}".format(m))
1381 LOG.debug(json_res)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001382 for item in json_res:
azvyagintsevf3515c82018-06-26 18:59:05 +03001383 vlans[item[sort_by]] = item
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001384 return vlans
1385
1386
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001387def get_fabricid(fabric):
azvyagintsevbca1f462018-05-25 19:06:46 +03001388 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001389 Get id for specific fabric
1390
1391 CLI Example:
1392
1393 .. code-block:: bash
1394
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001395 salt 'maas-node' maasng.get_fabricid fabric_name
azvyagintsevbca1f462018-05-25 19:06:46 +03001396 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001397 try:
1398 return list_fabric()[fabric]['id']
1399 except KeyError:
azvyagintsevefb6f5d2018-07-10 14:16:19 +03001400 return {"error": "Fabric not found on MaaS server"}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001401
1402
azvyagintsevf3515c82018-06-26 18:59:05 +03001403def check_vlan_in_fabric(fabric, vlan):
1404 """
1405 Check that VLAN exactly defined
1406 Return format:
1407 update - require update
1408 correct - fully coincides # not implemented
1409 not_exist - need's to be created
1410 """
1411
1412 ret = 'not_exist'
1413 vlans = list_vlans(fabric)
1414 if vlan in vlans.keys():
1415 LOG.debug("Requested VLAN:{} already exist"
1416 "in FABRIC:{}".format(vlan, fabric))
1417 ret = 'update'
1418 return ret
1419
1420
Petr Ruzicka80471852018-07-13 14:08:27 +02001421def create_vlan_in_fabric(name, fabric, vlan, description, primary_rack, mtu=1500,
azvyagintsevf3515c82018-06-26 18:59:05 +03001422 dhcp_on=False, update=False, vlan_id=""):
azvyagintsevbca1f462018-05-25 19:06:46 +03001423 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001424 Update vlan
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001425 CLI Example:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001426 .. code-block:: bash
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001427 salt 'maas-node' maasng.update_vlan name, fabric, vid, description, dhcp_on
azvyagintsevbca1f462018-05-25 19:06:46 +03001428 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001429 result = {}
1430
1431 data = {
1432 "name": name,
1433 "dhcp_on": str(dhcp_on),
1434 "description": description,
azvyagintsev6913e5e2018-07-05 11:42:53 +03001435 "primary_rack": list_racks()[primary_rack]['system_id'],
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001436 }
Michael Polenchukd25da792018-07-19 18:27:11 +04001437 if mtu:
1438 data['mtu'] = str(mtu)
azvyagintsevf3515c82018-06-26 18:59:05 +03001439 vlan = str(vlan)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001440 maas = _create_maas_client()
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001441 fabric_id = get_fabricid(fabric)
azvyagintsevf3515c82018-06-26 18:59:05 +03001442 try:
1443 if update:
1444 # MAAS have buggy logic here. Fallowing api reference, here should
1445 # be passed VID - which mean, API ID for vlan.
1446 # Otherwise, at least for maas 2.3.3-6498-ge4db91d exactly VLAN
1447 # should be passed. so, make temp.backward-convertation.
1448 # json_res = json.loads(maas.put(u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id,vlan_id), **data).read())
1449 json_res = json.loads(maas.put(
1450 u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id, vlan),
1451 **data).read())
1452 else:
1453 data['vid'] = str(vlan)
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001454 json_res = json.loads(maas.post(
1455 u'api/2.0/fabrics/{0}/vlans/'.format(fabric_id), None, **data).read())
azvyagintsevf3515c82018-06-26 18:59:05 +03001456 except Exception as inst:
1457 LOG.debug("create_vlan_in_fabric data:{}".format(data))
1458 try:
1459 m = inst.readlines()
1460 except:
1461 m = inst.message
1462 LOG.error("Message:{0}".format(m))
1463 result['result'] = False
1464 result['comment'] = 'Error updating vlan: {0}'.format(name)
1465 result['error'] = m
1466 return result
1467 LOG.debug("create_vlan_in_fabric:{}".format(json_res))
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001468 result["new"] = "Vlan {0} was updated".format(json_res["name"])
1469
1470 return result
azvyagintsevbca1f462018-05-25 19:06:46 +03001471
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001472
azvyagintsevf3515c82018-06-26 18:59:05 +03001473def check_subnet(cidr, name, fabric, gateway_ip):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001474 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001475 Check that subnet exactly defined
1476 Return format:
1477 update - require update
1478 correct - fully coincides # not implemented
1479 not_exist - need's to be created
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001480 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001481
1482 ret = 'not_exist'
1483 subnets = list_subnets(sort_by='cidr')
1484 if cidr in subnets.keys():
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001485 LOG.debug("Requested subnet cidr:{} already exist".format(cidr))
1486 ret = 'update'
azvyagintsevf3515c82018-06-26 18:59:05 +03001487 return ret
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001488
1489
azvyagintsevf3515c82018-06-26 18:59:05 +03001490def create_subnet(cidr='', name='', fabric='', gateway_ip='', vlan='',
1491 update=False, subnet_id=''):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001492 """
1493 Create subnet
1494
1495 CLI Example:
1496
1497 .. code-block:: bash
1498
1499 salt 'maas-node' maasng.create_subnet cidr, name, fabric, gateway_ip
1500 """
1501
1502 fabric_id = get_fabricid(fabric)
1503 result = {}
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001504 vlan = str(vlan)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001505 data = {
1506 "cidr": cidr,
1507 "name": name,
1508 "fabric": str(fabric_id),
1509 "gateway_ip": gateway_ip,
azvyagintsevf3515c82018-06-26 18:59:05 +03001510 'vlan': vlan,
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001511 }
1512 maas = _create_maas_client()
azvyagintsevf3515c82018-06-26 18:59:05 +03001513 # FIXME: vlan definition not work in 2.3.3-6498-ge4db91d.
1514 LOG.warning("Ignoring parameter vlan:{}".format(vlan))
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001515 data.pop('vlan', '')
azvyagintsevf3515c82018-06-26 18:59:05 +03001516 try:
1517 if update:
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001518 json_res = json.loads(
1519 maas.put(u"api/2.0/subnets/{0}/".format(subnet_id), **data).read())
azvyagintsevf3515c82018-06-26 18:59:05 +03001520 else:
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001521 json_res = json.loads(
1522 maas.post(u"api/2.0/subnets/", None, **data).read())
azvyagintsevf3515c82018-06-26 18:59:05 +03001523 except Exception as inst:
1524 LOG.debug("create_subnet data:{}".format(data))
1525 try:
1526 m = inst.readlines()
1527 except:
1528 m = inst.message
1529 LOG.error("Message:{0}".format(m))
1530 result['result'] = False
1531 result['comment'] = 'Error creating subnet: {0}'.format(name)
1532 result['error'] = m
1533 return result
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001534 LOG.debug("create_subnet:{}".format(json_res))
azvyagintsevf3515c82018-06-26 18:59:05 +03001535 result["new"] = "Subnet {0} with CIDR {1}" \
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001536 "and gateway {2} was created".format(
1537 name, cidr, gateway_ip)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001538
1539 return result
1540
1541
1542def get_subnet(subnet):
1543 """
1544 Get details for specific subnet
1545
1546 CLI Example:
1547
1548 .. code-block:: bash
1549
1550 salt 'maas-node' maasng.get_subnet subnet_name
1551 """
1552 try:
azvyagintsevf3515c82018-06-26 18:59:05 +03001553 return list_subnets()[subnet]
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001554 except KeyError:
1555 return {"error": "Subnet not found on MaaS server"}
1556
1557
1558def get_subnetid(subnet):
1559 """
1560 Get id for specific subnet
1561
1562 CLI Example:
1563
1564 .. code-block:: bash
1565
1566 salt 'maas-node' maasng.get_subnetid subnet_name
1567 """
1568 try:
azvyagintsevf3515c82018-06-26 18:59:05 +03001569 return list_subnets()[subnet]['id']
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001570 except KeyError:
1571 return {"error": "Subnet not found on MaaS server"}
1572
1573
1574def list_ipranges():
1575 """
1576 Get list of all ipranges from maas server
1577
1578 CLI Example:
1579
1580 .. code-block:: bash
1581
1582 salt 'maas-node' maasng.list_ipranges
1583 """
1584 ipranges = {}
1585 maas = _create_maas_client()
1586 json_res = json.loads(maas.get(u'api/2.0/ipranges/').read())
1587 for item in json_res:
1588 ipranges[item["start_ip"]] = item
1589 return ipranges
1590
1591
azvyagintsevf0904ac2018-07-05 18:53:26 +03001592def create_iprange(type_range, start_ip, end_ip, subnet=None, comment=None):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001593 """
1594 Create ip range
1595
1596 CLI Example:
1597
1598 .. code-block:: bash
1599
1600 salt 'maas-node' maasng.create_iprange type, start ip, end ip, comment
1601 """
1602 result = {}
1603
1604 data = {
1605 "type": type_range,
1606 "start_ip": start_ip,
1607 "end_ip": end_ip,
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001608 }
azvyagintsevf3515c82018-06-26 18:59:05 +03001609 if comment:
1610 data['comment'] = comment
azvyagintsevf0904ac2018-07-05 18:53:26 +03001611 if subnet:
1612 subnet_id = list_subnets()[subnet]['id']
1613 data['subnet'] = str(subnet_id)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001614 maas = _create_maas_client()
azvyagintsevf3515c82018-06-26 18:59:05 +03001615 _name = "Type:{}: {}-{}".format(type_range, start_ip, end_ip)
1616 try:
1617 json_res = json.loads(
1618 maas.post(u"api/2.0/ipranges/", None, **data).read())
1619 except Exception as inst:
1620 try:
1621 m = inst.readlines()
1622 except:
1623 m = inst.message
1624 LOG.error("Message:{0}".format(m))
1625 result['result'] = False
1626 result['comment'] = 'Error creating iprange:{0}'.format(_name)
1627 result['error'] = m
1628 return result
1629 result["new"] = "Iprange: {0} has been created".format(_name)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001630 LOG.debug("create_iprange:{}".format(json_res))
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001631
1632 return result
1633
1634
1635def get_iprangeid(start_ip):
1636 """
1637 Get id for ip range from maas server
1638
1639 CLI Example:
1640
1641 .. code-block:: bash
1642
1643 salt 'maas-node' maasng.get_iprangeid start_ip
1644 """
1645 try:
1646 return list_ipranges()[start_ip]['id']
1647 except KeyError:
1648 return {"error": "Ip range not found on MaaS server"}
1649
1650
1651def get_startip(start_ip):
1652 """
1653 Get start ip for ip range
1654
1655 CLI Example:
1656
1657 .. code-block:: bash
1658
1659 salt 'maas-node' maasng.get_startip start ip
1660 """
1661 try:
1662 return list_ipranges()[start_ip]
1663 except KeyError:
1664 return {"error": "Ip range not found on MaaS server"}
azvyagintsevbca1f462018-05-25 19:06:46 +03001665# END NETWORKING
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001666
1667# MAAS CONFIG SECTION
1668
1669
azvyagintsev58947072018-06-29 12:09:48 +03001670def _getHTTPCode(url):
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001671 code = '003'
1672 m = ''
azvyagintsev58947072018-06-29 12:09:48 +03001673 try:
1674 connection = urllib2.urlopen(url)
1675 code = connection.getcode()
1676 connection.close()
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001677 except (urllib2.HTTPError, urllib2.URLError) as e:
1678 try:
1679 code = e.getcode()
1680 except:
1681 m = e.reason
1682 pass
azvyagintsevf3515c82018-06-26 18:59:05 +03001683 LOG.debug("Unexpected http code:{} from "
1684 "url:{}\nwith message:{}".format(code, url, m))
azvyagintsev58947072018-06-29 12:09:48 +03001685 pass
1686 return code
1687
1688
1689def wait_for_http_code(url=None, expected=[200]):
1690 """
1691 Simple function, which just wait for avaible api, aka wait for 200.
1692
1693 CLI Example:
1694
1695 .. code-block:: bash
1696
1697 salt 'maas-node' maasng.wait_for_http_code url expected=[200]
1698
1699 """
1700 ret = {}
1701 started_at = time.time()
1702 poll_time = 5
1703 timeout = 60 * 2
1704 while _getHTTPCode(url) not in expected:
1705 c_timeout = timeout - (time.time() - started_at)
1706 if c_timeout <= 0:
1707 ret['result'] = False
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001708 ret["comment"] = "api:{} not answered in time".format(url)
azvyagintsev58947072018-06-29 12:09:48 +03001709 return ret
1710 LOG.info(
1711 "Waiting for api:{0}\n"
1712 "sleep for:{1}s "
1713 "Left:{2}/{3}s".format(url, poll_time, round(c_timeout),
1714 timeout))
1715 time.sleep(poll_time)
1716 ret['result'] = True
1717 ret["comment"] = "MAAS API:{} up.".format(url)
1718 return ret
1719
1720
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001721def _get_boot_source_id_by_url(url):
1722 # FIXME: fix ret\validation
1723 try:
1724 bs_id = get_boot_source(url=url)["id"]
1725 except KeyError:
1726 return {"error": "boot-source:{0} not exist!".format(url)}
1727 return bs_id
1728
1729
1730def get_boot_source(url=None):
1731 """
1732 Read a boot source by url. If url not specified - return all.
1733
1734 CLI Example:
1735
1736 .. code-block:: bash
1737
1738 salt 'maas-node' maasng.get_boot_source url
1739
1740 """
1741 boot_sources = {}
1742 maas = _create_maas_client()
1743 json_res = json.loads(maas.get(u'api/2.0/boot-sources/').read() or 'null')
1744 for item in json_res:
1745 boot_sources[str(item["url"])] = item
1746 if url:
1747 return boot_sources.get(url, {})
1748 return boot_sources
1749
1750
1751def delete_boot_source(url, bs_id=None):
1752 """
1753 Delete a boot source by url.
1754
1755 CLI Example:
1756
1757 .. code-block:: bash
1758
1759 sal 'maas-node' maasng.delete url
1760
1761 """
1762 result = {}
1763 if not bs_id:
1764 bs_id = _get_boot_source_id_by_url(url)
1765 maas = _create_maas_client()
1766 json_res = json.loads(maas.delete(
1767 u'/api/2.0/boot-sources/{0}/'.format(bs_id)).read() or 'null')
1768 LOG.debug("delete_boot_source:{}".format(json_res))
1769 result["new"] = "Boot-resource {0} deleted".format(url)
1770 return result
1771
1772
1773def boot_sources_delete_all_others(except_urls=[]):
1774 """
1775 Delete all boot-sources, except defined in 'except_urls' list.
1776 """
azvyagintseve2e37a12018-11-01 14:45:49 +02001777 result = {'changes': {}}
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001778 maas_boot_sources = get_boot_source()
1779 if 0 in [len(except_urls), len(maas_boot_sources)]:
1780 result['result'] = None
azvyagintseve2e37a12018-11-01 14:45:49 +02001781 result["comment"] = "'except_urls' or 'maas boot-sources' for " \
1782 "delete empty. No changes goinng to be."
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001783 return result
azvyagintseve2e37a12018-11-01 14:45:49 +02001784 # FIXME: fix 'changes' accumulator
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001785 for url in maas_boot_sources.keys():
1786 if url not in except_urls:
1787 LOG.info("Removing boot-source:{}".format(url))
1788 boot_resources_import(action='stop_import', wait=True)
1789 result["changes"] = delete_boot_source(url)
1790 return result
1791
1792
1793def create_boot_source(url, keyring_filename='', keyring_data='', wait=False):
1794 """
1795 Create and import maas boot-source: link to maas-ephemeral repo
1796 Be aware, those step will import resource to rack ctrl, but you also need to import
1797 them into the region!
1798
1799
1800 :param url: The URL of the BootSource.
1801 :param keyring_filename: The path to the keyring file for this BootSource.
1802 :param keyring_data: The GPG keyring for this BootSource, base64-encoded data.
1803
1804 """
1805
1806 # TODO: not work with 'update' currently => keyring update may fail.
1807 result = {}
1808
1809 data = {
1810 "url": url,
1811 "keyring_filename": keyring_filename,
1812 "keyring_data": str(keyring_data),
1813 }
1814
1815 maas = _create_maas_client()
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001816 if url in get_boot_source():
1817 result['result'] = None
1818 result["comment"] = "boot resource already exist"
1819 return result
1820
1821 # NOTE: maas.post will return 400, if url already defined.
1822 json_res = json.loads(
1823 maas.post(u'api/2.0/boot-sources/', None, **data).read())
1824 if wait:
1825 LOG.debug(
1826 "Sleep for 5s,to get MaaS some time to process previous request")
1827 time.sleep(5)
1828 ret = boot_resources_is_importing(wait=True)
1829 if ret is dict:
1830 return ret
1831 LOG.debug("create_boot_source:{}".format(json_res))
1832 result["new"] = "boot resource {0} was created".format(json_res["url"])
1833
1834 return result
1835
1836
1837def boot_resources_import(action='import', wait=False):
1838 """
1839 import/stop_import the boot resources.
1840
1841 :param action: import\stop_import
1842 :param wait: True\False. Wait till process finished.
1843
1844 CLI Example:
1845
1846 .. code-block:: bash
1847
1848 salt 'maas-node' maasng.boot_resources_import action='import'
1849
1850 """
1851 maas = _create_maas_client()
1852 # Have no idea why, but usual jsonloads not work here..
1853 imp = maas.post(u'api/2.0/boot-resources/', action)
1854 if imp.code == 200:
1855 LOG.debug('boot_resources_import:{}'.format(imp.readline()))
1856 if wait:
1857 boot_resources_is_importing(wait=True)
1858 return True
1859 else:
1860 return False
1861
1862
1863def boot_resources_is_importing(wait=False):
1864 maas = _create_maas_client()
1865 result = {}
1866 if wait:
1867 started_at = time.time()
1868 poll_time = 5
1869 timeout = 60 * 15
1870 while boot_resources_is_importing(wait=False):
1871 c_timeout = timeout - (time.time() - started_at)
1872 if c_timeout <= 0:
1873 result['result'] = False
1874 result["comment"] = "Boot-resources import not finished in time"
1875 return result
1876 LOG.info(
1877 "Waiting boot-resources import done\n"
1878 "sleep for:{}s "
1879 "Left:{}/{}s".format(poll_time, round(c_timeout), timeout))
1880 time.sleep(poll_time)
1881 return json.loads(
1882 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1883 else:
1884 return json.loads(
1885 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1886
1887#####
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001888# def boot_sources_selections_delete_all_others(except_urls=[]):
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001889# """
1890# """
1891# result = {}
1892# return result
1893
1894
1895def is_boot_source_selections_in(dict1, list1):
1896 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001897 Check that requested boot-selection already in maas bs selections,
1898 if True- return bss id.
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001899 # FIXME: those hack check doesn't look good.
1900 """
1901 for bs in list1:
1902 same = set(dict1.keys()) & set(bs.keys())
1903 if all(elem in same for elem in
1904 ['os', 'release', 'arches', 'subarches', 'labels']):
azvyagintsevf3515c82018-06-26 18:59:05 +03001905 LOG.debug("boot-selection in maas:{0}\n"
1906 "looks same to requested:{1}".format(bs, dict1))
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001907 return bs['id']
1908 return False
1909
1910
1911def get_boot_source_selections(bs_url):
1912 """
1913 Get boot-source selections.
1914 """
1915 # check for key_error!
1916 bs_id = _get_boot_source_id_by_url(bs_url)
1917 maas = _create_maas_client()
1918 json_res = json.loads(
1919 maas.get(u'/api/2.0/boot-sources/{0}/selections/'.format(bs_id)).read())
1920 LOG.debug(
1921 "get_boot_source_selections for url:{} \n{}".format(bs_url, json_res))
1922 return json_res
1923
1924
1925def create_boot_source_selections(bs_url, os, release, arches="*",
1926 subarches="*", labels="*", wait=True):
1927 """
1928 Create a new boot source selection for bs_url.
1929 :param os: The OS (e.g. ubuntu, centos) for which to import resources.Required.
1930 :param release: The release for which to import resources. Required.
1931 :param arches: The architecture list for which to import resources.
1932 :param subarches: The subarchitecture list for which to import resources.
1933 :param labels: The label lists for which to import resources.
1934 """
1935
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001936 result = {"result": True, 'name': bs_url, 'changes': None}
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001937
1938 data = {
1939 "os": os,
1940 "release": release,
1941 "arches": arches,
1942 "subarches": subarches,
1943 "labels": labels,
1944 }
1945
1946 maas = _create_maas_client()
1947 bs_id = _get_boot_source_id_by_url(bs_url)
1948 # TODO add pre-create verify
1949 maas_bs_s = get_boot_source_selections(bs_url)
1950 if is_boot_source_selections_in(data, maas_bs_s):
1951 result["result"] = True
azvyagintsevf3515c82018-06-26 18:59:05 +03001952 result["comment"] = 'Requested boot-source selection ' \
1953 'for {0} already exist.'.format(
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001954 bs_url)
1955 return result
1956
1957 # NOTE: maas.post will return 400, if url already defined.
azvyagintsevcb54d142018-06-19 16:18:32 +03001958 # Also, maas need's some time to import info about stream.
1959 # unfortunatly, maas don't have any call to check stream-import-info - so, we need to implement
1960 # at least simple retry ;(
1961 json_res = False
1962 poll_time = 5
azvyagintseve2e37a12018-11-01 14:45:49 +02001963 for i in range(0, 10):
azvyagintsevcb54d142018-06-19 16:18:32 +03001964 try:
1965 json_res = json.loads(
1966 maas.post(u'api/2.0/boot-sources/{0}/selections/'.format(bs_id), None,
1967 **data).read())
1968 except Exception as inst:
1969 m = inst.readlines()
azvyagintsevf3515c82018-06-26 18:59:05 +03001970 LOG.warning("boot_source_selections "
1971 "catch error during processing. Most-probably, "
azvyagintseve2e37a12018-11-01 14:45:49 +02001972 "streams data not imported yet.\nSleep:{}s "
1973 "Retry:{}/10".format(poll_time, i))
azvyagintsevcb54d142018-06-19 16:18:32 +03001974 LOG.warning("Message:{0}".format(m))
1975 time.sleep(poll_time)
1976 continue
1977 break
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001978 LOG.debug("create_boot_source_selections:{}".format(json_res))
azvyagintsevcb54d142018-06-19 16:18:32 +03001979 if not json_res:
1980 result["result"] = False
azvyagintsevf3515c82018-06-26 18:59:05 +03001981 result["comment"] = 'Failed to create requested boot-source selection' \
1982 ' for {0}.'.format(bs_url)
azvyagintsevcb54d142018-06-19 16:18:32 +03001983 return result
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001984 if wait:
1985 LOG.debug(
1986 "Sleep for 5s,to get MaaS some time to process previous request")
1987 time.sleep(5)
1988 ret = boot_resources_import(action='import', wait=True)
1989 if ret is dict:
1990 return ret
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001991 result["comment"] = "boot-source selection for {0} was created".format(
1992 bs_url)
azvyagintsevcb54d142018-06-19 16:18:32 +03001993 result["new"] = data
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001994
1995 return result
1996
1997# END MAAS CONFIG SECTION
azvyagintsevcb54d142018-06-19 16:18:32 +03001998
1999# RACK CONTROLLERS SECTION
2000
2001
2002def get_rack(hostname):
2003 """
2004 Get information about specified rackd
2005
2006 CLI Example:
2007
2008 .. code-block:: bash
2009
2010 salt-call maasng.get_rack rack_hostname
2011 """
2012 try:
2013 return list_racks()[hostname]
2014 except KeyError:
2015 return {"error": "rack:{} not found on MaaS server".format(hostname)}
2016
2017
azvyagintsev6913e5e2018-07-05 11:42:53 +03002018def list_racks(sort_by='hostname'):
azvyagintsevcb54d142018-06-19 16:18:32 +03002019 """
2020 Get list of all rack controllers from maas server
2021
2022 CLI Example:
2023
2024 .. code-block:: bash
2025
2026 salt-call maasng.list_racks
2027 """
2028 racks = {}
2029 maas = _create_maas_client()
2030 json_res = json.loads(
2031 maas.get(u"/api/2.0/rackcontrollers/").read() or 'null')
2032 for item in json_res:
azvyagintsev6913e5e2018-07-05 11:42:53 +03002033 racks[item[sort_by]] = item
azvyagintsevcb54d142018-06-19 16:18:32 +03002034 return racks
2035
2036
2037def sync_bs_to_rack(hostname=None):
2038 """
2039 Sync RACK boot-sources with REGION. If no hostname probided - sync to all.
2040
2041 CLI Example:
2042
2043 .. code-block:: bash
2044
2045 salt-call maasng.sync_bs_to_rack rack_hostname
2046 """
2047 ret = {}
2048 maas = _create_maas_client()
2049 if not hostname:
2050 LOG.info("boot-sources sync initiated for ALL Rack's")
2051 # Convert to json-like format
2052 json_res = json.loads('["{0}"]'.format(
2053 maas.post(u"/api/2.0/rackcontrollers/",
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 ALL Rack's"
2058 return ret
2059 LOG.info("boot-sources sync initiated for RACK:{0}".format(hostname))
2060 # Convert to json-like format
2061 json_res = json.loads('["{0}"]'.format(maas.post(
2062 u"/api/2.0/rackcontrollers/{0}/".format(
2063 get_rack(hostname)['system_id']),
2064 'import_boot_images').read()))
2065 LOG.debug("sync_bs_to_rack:{}".format(json_res))
2066 ret['result'] = True
2067 ret['comment'] = "boot-sources sync initiated for {0} Rack's".format(
2068 hostname)
2069 return
2070
2071
2072def rack_list_boot_imgs(hostname):
2073 ret = {}
2074 maas = _create_maas_client()
2075 LOG.debug("rack_list_boot_imgs:{}".format(hostname))
2076 ret = json.loads(maas.get(u"/api/2.0/rackcontrollers/{0}/".format(
2077 get_rack(hostname)['system_id']), 'list_boot_images').read() or 'null')
2078 return ret
2079
2080
2081def is_rack_synced(hostname):
2082 rez = rack_list_boot_imgs(hostname)['status']
2083 if rez == 'synced':
2084 return True
2085 return False
2086
2087# TODO do we actually need _exact_ check per-pack?
2088# def wait_for_images_on_rack(hostname):
2089#
2090# """
2091# WA function, to be able check that RACK actually done SYNC images
2092# for REQUIRED images at least.
2093# Required image to be fetched from
2094# reclass:maas:region:boot_sources_selections:[keys]:os/release formation
2095#
2096# CLI Example:
2097#
2098# .. code-block:: bash
2099#
2100# salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
2101# """
2102# try:
2103# bss = __salt__['config.get']('maas')['region']['boot_sources_selections']
2104# except KeyError:
2105# ret['result'] = None
2106# ret['comment'] = "boot_sources_selections definition for sync not found."
2107# return ret
2108# s_names = []
2109# # Format u'name': u'ubuntu/xenial'
2110# for v in bss.values():s_names.append("{0}/{1}".format(v['os'],v['release']))
2111# # Each names, should be in rack and whole rack should be in sync-ed state
2112
2113
2114def sync_and_wait_bs_to_all_racks():
2115 """
2116 Sync ALL rack's with regions source images.
2117
2118 CLI Example:
2119
2120 .. code-block:: bash
2121
2122 salt-call maasng.sync_and_wait_bs_to_all_racks
2123 """
2124 sync_bs_to_rack()
2125 for rack in list_racks().keys():
2126 wait_for_sync_bs_to_rack(hostname=rack)
2127 return True
2128
2129
2130def wait_for_sync_bs_to_rack(hostname=None):
2131 """
2132 Wait for boot images sync finished, on exact rack
2133
2134 CLI Example:
2135
2136 .. code-block:: bash
2137
2138 salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
2139 """
2140 ret = {}
2141 started_at = time.time()
2142 poll_time = 5
2143 timeout = 60 * 15
2144 while not is_rack_synced(hostname):
2145 c_timeout = timeout - (time.time() - started_at)
2146 if c_timeout <= 0:
2147 ret['result'] = False
2148 ret[
2149 "comment"] = "Boot-resources sync on rackd:{0}" \
2150 "not finished in time".format(
2151 hostname)
2152 return ret
2153 LOG.info(
2154 "Waiting boot-resources sync done to rack:{0}\n"
2155 "sleep for:{1}s "
2156 "Left:{2}/{3}s".format(hostname, poll_time, round(c_timeout),
2157 timeout))
2158 time.sleep(poll_time)
2159 ret['result'] = is_rack_synced(hostname)
2160 ret["comment"] = "Boot-resources sync on rackd:{0} finished".format(
2161 hostname)
2162 return ret
2163
2164# END RACK CONTROLLERS SECTION
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02002165# SSHKEYS
2166
2167
2168def list_sshkeys():
2169 """
2170 Get list of all sshkeys
2171
2172 CLI Example:
2173
2174 .. code-block:: bash
2175
2176 salt 'maas-node' maasng.list_sshkeys
2177 salt-call maasng.list_sshkeys
2178 """
2179 ssh = {}
2180 maas = _create_maas_client()
2181 json_res = json.loads(maas.get(u'api/2.0/account/prefs/sshkeys/').read())
2182 LOG.info(json_res)
2183 for item in json_res:
2184 ssh[item["key"]] = item
2185 return ssh
2186
2187
2188def add_sshkey(sshkey):
2189 """
2190 Add SSH key for user to MAAS.
2191
2192 CLI Example:
2193
2194 .. code-block:: bash
2195
2196 salt 'maas-node' maasng.add_sshkey sshkey
2197 salt-call maasng.add_sshkey sshkey
2198 """
2199 data = {
2200 "key": sshkey,
2201 }
2202 result = {}
2203 maas = _create_maas_client()
2204
2205 maas.post(u"/api/2.0/account/prefs/sshkeys/", None, **data).read()
2206 result["new"] = "SSH Key {0} was added.".format(sshkey)
2207
2208 return result
2209
2210
2211def get_sshkey(sshkey):
2212 """
2213 Get start ip for ip range
2214
2215 CLI Example:
2216
2217 .. code-block:: bash
2218
2219 salt 'maas-node' maasng.get_sshkey sshkey
2220 salt-call maasng.get_sshkey sshkey
2221 """
2222 try:
2223 return list_sshkeys()[sshkey]
2224 except KeyError:
2225 return {"error": "SSH key not found on MaaS server"}
2226# END SSHKEYS