blob: d07f9bd91593c146ca675c092552b6150846ec51 [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 """
Taras Khlivnyak312f0b12021-03-18 08:39:18 +0200168 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100169 try:
Taras Khlivnyak312f0b12021-03-18 08:39:18 +0200170 return json.loads(maas.get(u'api/2.0/machines/?hostname='+hostname).read())[0]
171 except IndexError:
Alexei Lugovoie5b64122018-11-06 12:30:01 +0100172 return {"error":
173 { 0: "Machine not found" }
174 }
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100175
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
Serhii Lystopad6f2fca92020-05-17 19:32:01 +0300504def create_partition(hostname, disk, size, fs_type=None, mount=None, mount_options=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 }
Serhii Lystopad6f2fca92020-05-17 19:32:01 +0300554 if mount_options:
555 data["mount_options"] = mount_options
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100556
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200557 # TODO validation
558 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
559 system_id, device_id, str(partition['id'])), "mount", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100560 LOG.info(json_res)
561 result["mount"] = "Mount point {0} created".format(mount)
562
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100563 return result
564
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200565
Dzmitry Stremkouskid95bd2e2018-12-03 17:35:46 +0100566def list_dnsresources():
567 """
568 List DNS resources known to MAAS.
569
570 CLI Example:
571
572 .. code-block:: bash
573
574 salt-call maasng.list_dnsresources
575
576 """
577 result = {}
578 res_json = []
579 maas = _create_maas_client()
580
581 # TODO validation
582 result = json.loads(maas.get(u"api/2.0/dnsresources/").read())
583 for elem in result:
584 ip_addresses = []
585 for ip in elem["ip_addresses"]:
586 ip_addresses.append(ip["ip"])
587 res_json.append(
588 {
589 "ip_addresses": ip_addresses,
590 "id": elem["id"],
591 "fqdn": elem["fqdn"],
592 "hostname": elem["fqdn"].split(".")[0]
593 }
594 )
595
596 LOG.debug(res_json)
597
598 return res_json
599
600
601def list_ipaddresses():
602 """
603 List IP addresses known to MAAS.
604
605 CLI Example:
606
607 .. code-block:: bash
608
609 salt-call maasng.list_ipaddresses
610
611 """
612 result = {}
613 res_json = []
614 maas = _create_maas_client()
615
616 # TODO validation
617 result = json.loads(maas.get(u"api/2.0/ipaddresses/?all").read())
618 for elem in result:
619 res_json.append(
620 {
621 "ip": elem["ip"],
622 "owner": { "username": elem["owner"]["username"] },
623 "created": elem["created"],
624 "alloc_type_name": elem["alloc_type_name"],
625 "alloc_type": elem["alloc_type"],
626 "subnet": {
627 "id": elem["subnet"]["id"],
628 "cidr": elem["subnet"]["cidr"],
629 "name": elem["subnet"]["name"]
630 }
631 }
632 )
633
634 LOG.debug(res_json)
635
636 return res_json
637
638
639def reserve_ipaddress(hostname,subnet,ip=""):
640 """
641 Reserve IP address for specified hostname in specified subnet
642
643 CLI Example:
644
645 .. code-block:: bash
646
647 salt-call maasng.reserve_ipaddress hostname 192.168.0.0/24 192.168.0.254
648 salt-call maasng.reserve_ipaddress hostname 192.168.0.0/24
649
650 """
651 result = {}
652 data = {}
653 maas = _create_maas_client()
654
655 data = {
656 "subnet": subnet,
657 "hostname": hostname
658 }
659
660 if ip:
661 data["ip"] = ip
662
663 # TODO validation
664 result = json.loads(maas.post(u"api/2.0/ipaddresses/", "reserve", **data).read())
665 res_json = {
666 "created": result["created"],
667 "type": "DNS",
668 "hostname": hostname,
669 "ip": result["ip"]
670 }
671
672 LOG.info(res_json)
673
674 return res_json
675
676
677def release_ipaddress(ipaddress):
678 """
679 Release an IP address that was previously reserved by the user.
680
681 CLI Example:
682
683 .. code-block:: bash
684
685 salt-call maasng.release_ipaddress 192.168.2.10
686
687 """
688 result = {}
689 data = {}
690 maas = _create_maas_client()
691
692 data = {
693 "ip": ipaddress
694 }
695
696 # TODO validation
697 return maas.post(u"api/2.0/ipaddresses/", "release", **data).read()
698
699
700def sync_address_pool():
701 """
702 Manage address pool for ext_pillar.
703
704 CLI Example:
705
706 .. code-block:: bash
707
708 salt-call maasng.sync_address_pool
709
710 """
711
712 address_pool = __pillar__["address_pool"]
713 LOG.debug("Address pool:")
714 LOG.debug(address_pool)
715
716 cluster_networks = __pillar__["cluster_networks"]
717 LOG.debug("Cluster networks:")
718 LOG.debug(cluster_networks)
719
720 dnsresources = list_dnsresources()
721 LOG.debug("DNS resources:")
722 LOG.debug(dnsresources)
723
724 machines = list_machines()
725 LOG.debug("Machines:")
726 LOG.debug(machines)
727
728 for net in address_pool:
729 if net == "external":
730 continue
731 for addr in address_pool[net]['pool']:
732 ipaddr = address_pool[net]['pool'][addr]
733 if ipaddr == "":
734 LOG.debug('Releasing IP address for: ' + addr)
735 release_required = False
736 for elem in dnsresources:
737 if elem["hostname"] == addr:
738 release_required = True
739 ip_addresses = elem["ip_addresses"]
740 if release_required:
741 for ip in ip_addresses:
742 res = release_ipaddress(ip)
743 LOG.debug(res)
744 else:
745 LOG.debug('IP for ' + addr + ' already released')
746 elif is_valid_ipv6(ipaddr) or is_valid_ipv4(ipaddr):
747 LOG.debug('Ensure static IP address "' + ipaddr + '" for ' + addr)
748 reserve_required = True
749 for elem in dnsresources:
750 if elem["hostname"] == addr:
751 reserve_required = False
752 for elem, elemval in machines.iteritems():
753 for iface in elemval["interface_set"]:
754 for link in iface["links"]:
755 if "ip_address" in link:
756 if link["ip_address"] == ipaddr:
757 reserve_required = False
758 if reserve_required:
759 res = reserve_ipaddress(addr, cluster_networks[net]['cidr'], ipaddr)
760 reserve_required = False
761 LOG.debug(res)
762 else:
763 LOG.debug('Static IP address "' + ipaddr + '" for ' + addr + ' ensured')
764 else:
765 LOG.debug('Requesting IP address for' + addr)
766 reserve_required = True
767 for elem in dnsresources:
768 if elem["hostname"] == addr:
769 reserve_required = False
770 ip = elem["ip_addresses"][0]
771 if reserve_required:
772 res = reserve_ipaddress(addr, cluster_networks[net]['cidr'])
773 LOG.debug(res)
774 else:
775 LOG.debug(addr + " already has IP " + ip)
776
777 return True
778
779
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100780def delete_partition(hostname, disk, partition_name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300781 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100782 Delete partition on device.
783
784 CLI Example:
785
786 .. code-block:: bash
787
788 salt 'maas-node' maasng.delete_partition server_hostname disk_name partition_name
789 salt-call maasng.delete_partition server_hostname disk_name partition_name
790
791 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300792 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100793 result = {}
794 data = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200795 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100796 system_id = get_machine(hostname)["system_id"]
797 LOG.info(system_id)
798
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200799 device_id = _get_blockdevice_id_by_name(hostname, disk)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100800 LOG.info(device_id)
801
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200802 partition_id = _get_partition_id_by_name(hostname, disk, partition_name)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100803
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200804 maas.delete(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
805 system_id, device_id, partition_id)).read()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100806 result["new"] = "Partition {0} deleted".format(partition_name)
807 return result
808
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200809
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100810def delete_partition_by_id(hostname, disk, partition_id):
azvyagintsevbca1f462018-05-25 19:06:46 +0300811 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100812 Delete partition on device. Partition spefified by id of parition
813
814 CLI Example:
815
816 .. code-block:: bash
817
818 salt 'maas-node' maasng.delete_partition_by_id server_hostname disk_name partition_id
819 salt-call maasng.delete_partition_by_id server_hostname disk_name partition_id
820
821 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300822 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100823 result = {}
824 data = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200825 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100826 system_id = get_machine(hostname)["system_id"]
827 LOG.info(system_id)
828
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200829 device_id = _get_blockdevice_id_by_name(hostname, disk)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100830 LOG.info(device_id)
831
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200832 maas.delete(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
833 system_id, device_id, partition_id)).read()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100834 result["new"] = "Partition {0} deleted".format(partition_id)
835 return result
azvyagintsevbca1f462018-05-25 19:06:46 +0300836# END PARTITIONS
837# DISK LAYOUT
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100838
azvyagintsevbca1f462018-05-25 19:06:46 +0300839
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200840def drop_storage_schema(hostname, disk=None):
azvyagintsevbca1f462018-05-25 19:06:46 +0300841 """
842 #1. Drop lv
843 #2. Drop vg
844 #3. Drop md # need to zero-block?
845 #3. Drop part
846 """
847
848 if __opts__['test']:
849 ret['result'] = None
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200850 ret['comment'] = 'Storage schema on {0} will be removed'.format(
851 hostname)
azvyagintsevbca1f462018-05-25 19:06:46 +0300852 return ret
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200853 # TODO validation if exists
azvyagintsevbca1f462018-05-25 19:06:46 +0300854 vgs = list_volume_groups(hostname)
855 for vg in vgs:
856 delete_volume_group(hostname, vg)
857
858 raids = list_raids(hostname)
859 for raid in raids:
860 delete_raid(hostname, raid)
861
862 blocks = list_blockdevices(hostname)
863 for block_d in blocks:
864 partitions = __salt__['maasng.list_partitions'](hostname, block_d)
865 for partition_name, partition in partitions.iteritems():
866 LOG.info('delete partition:\n{}'.format(partition))
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200867 __salt__['maasng.delete_partition_by_id'](
868 hostname, block_d, partition["id"])
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200869
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100870
871def 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 +0300872 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100873 Update disk layout. Flat or LVM layout supported.
874
875 CLI Example:
876
877 .. code-block:: bash
878
879 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
880 salt-call maasng.update_disk_layout server_hostname lvm root_size=None, root_device=None, volume_group=None, volume_name=None, volume_size=None
881
882 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300883 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100884 result = {}
885 data = {
886 "storage_layout": layout,
887 }
888
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200889 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100890 system_id = get_machine(hostname)["system_id"]
891 LOG.info(system_id)
892
azvyagintsevbca1f462018-05-25 19:06:46 +0300893 if layout == 'custom':
894 drop_storage_schema(hostname)
895 result["new"] = {
896 "storage_layout": layout,
897 }
898
899 return result
900
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100901 if root_size != None:
902 bit_size = str(root_size * 1073741824)
903 LOG.info(bit_size)
904 data["root_size"] = bit_size
905
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100906 if root_device != None:
907 LOG.info(root_device)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200908 data["root_device"] = str(
909 _get_blockdevice_id_by_name(hostname, root_device))
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100910
911 if layout == 'lvm':
912 if volume_group != None:
913 LOG.info(volume_group)
914 data["vg_name"] = volume_group
915 if volume_name != None:
916 LOG.info(volume_name)
917 data["lv_name"] = volume_name
918 if volume_size != None:
919 vol_size = str(volume_size * 1073741824)
920 LOG.info(vol_size)
921 data["lv_size"] = vol_size
922
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200923 # TODO validation
924 json_res = json.loads(maas.post(
925 u"api/2.0/machines/{0}/".format(system_id), "set_storage_layout", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100926 LOG.info(json_res)
927 result["new"] = {
928 "storage_layout": layout,
929 }
930
931 return result
932
azvyagintsevbca1f462018-05-25 19:06:46 +0300933# END DISK LAYOUT
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200934# LVM
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100935
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200936
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100937def list_volume_groups(hostname):
azvyagintsevbca1f462018-05-25 19:06:46 +0300938 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100939 Get list of all volume group on machine.
940
941 CLI Example:
942
943 .. code-block:: bash
944
945 salt 'maas-node' maasng.list_volume_groups server_hostname
946 salt-call maasng.list_volume_groups server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300947 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100948 volume_groups = {}
949
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200950 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100951 system_id = get_machine(hostname)["system_id"]
952 LOG.info(system_id)
953
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200954 # TODO validation if exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100955
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200956 json_res = json.loads(
957 maas.get(u"api/2.0/nodes/{0}/volume-groups/".format(system_id)).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100958 LOG.info(json_res)
959 for item in json_res:
960 volume_groups[item["name"]] = item
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200961 # return
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100962 return volume_groups
963
964
965def get_volume_group(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300966 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100967 Get information about specific volume group on machine.
968
969 CLI Example:
970
971 .. code-block:: bash
972
973 salt 'maas-node' maasng.list_blockdevices server_hostname
974 salt-call maasng.list_blockdevices server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300975 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200976 # TODO validation that exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100977 return list_volume_groups(hostname)[name]
978
979
980def create_volume_group(hostname, volume_group_name, disks=[], partitions=[]):
azvyagintsevbca1f462018-05-25 19:06:46 +0300981 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100982 Create new volume group on machine. Disks or partitions needs to be provided.
983
984 CLI Example:
985
986 .. code-block:: bash
987
988 salt 'maas-node' maasng.create_volume_group volume_group_name, disks=[sda,sdb], partitions=[]
989 salt-call maasng.create_volume_group server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300990 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100991 result = {}
992
993 data = {
994 "name": volume_group_name,
995 }
996
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200997 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100998 system_id = get_machine(hostname)["system_id"]
999 LOG.info(system_id)
1000
1001 disk_ids = []
1002 partition_ids = []
1003
1004 for disk in disks:
1005 p_disk = get_blockdevice(hostname, disk)
1006 if p_disk["partition_table_type"] == None:
1007 disk_ids.append(str(p_disk["id"]))
1008 else:
azvyagintsevf3515c82018-06-26 18:59:05 +03001009 result["error"] = "Device {0} on" \
1010 "machine {1} cointains partition" \
1011 "table".format(disk, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001012 return result
1013
1014 for partition in partitions:
1015 try:
1016 device = partition.split("-")[0]
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001017 device_part = list_partitions(hostname, device)
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001018 partition_ids.append(str(device_part[partition]["id"]))
1019 except KeyError:
azvyagintsevf3515c82018-06-26 18:59:05 +03001020 result["error"] = "Partition {0} does" \
1021 "not exists on " \
1022 "machine {1}".format(partition, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001023 return result
1024
1025 data["block_devices"] = disk_ids
1026 data["partitions"] = partition_ids
1027 LOG.info(partition_ids)
1028 LOG.info(partitions)
1029
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001030 # TODO validation
1031 json_res = json.loads(maas.post(
1032 u"api/2.0/nodes/{0}/volume-groups/".format(system_id), None, **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001033 LOG.info(json_res)
1034 result["new"] = "Volume group {0} created".format(json_res["name"])
1035
1036 return result
1037
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001038
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001039def delete_volume_group(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +03001040 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001041 Delete volume group on machine.
1042
1043 CLI Example:
1044
1045 .. code-block:: bash
1046
1047 salt 'maas-node' maasng.delete_volume_group server_hostname vg0
1048 salt-call maasng.delete_volume_group server_hostname vg0
azvyagintsevbca1f462018-05-25 19:06:46 +03001049 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001050
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001051 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001052 system_id = get_machine(hostname)["system_id"]
azvyagintsevbca1f462018-05-25 19:06:46 +03001053 LOG.debug('delete_volume_group:{}'.format(system_id))
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001054
azvyagintsevbca1f462018-05-25 19:06:46 +03001055 vg_id = str(_get_volume_group_id_by_name(hostname, name))
1056 for vol in get_volumes(hostname, name):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001057 delete_volume(hostname, vol, name)
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001058
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001059 # TODO validation
1060 json_res = json.loads(maas.delete(
1061 u"api/2.0/nodes/{0}/volume-group/{1}/".format(system_id, vg_id)).read() or 'null')
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001062 LOG.info(json_res)
1063
1064 return True
1065
1066
azvyagintsevf3515c82018-06-26 18:59:05 +03001067def create_volume(hostname, volume_name, volume_group, size, fs_type=None,
Serhii Lystopad6f2fca92020-05-17 19:32:01 +03001068 mount=None, mount_options=None):
azvyagintsevbca1f462018-05-25 19:06:46 +03001069 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001070 Create volume on volume group.
1071
1072 CLI Example:
1073
1074 .. code-block:: bash
1075
1076 salt 'maas-node' maasng.create_volume server_hostname volume_name, volume_group, size, fs_type=None, mount=None
1077 salt-call maasng.create_volume server_hostname volume_name, volume_group, size, fs_type=None, mount=None
azvyagintsevbca1f462018-05-25 19:06:46 +03001078 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001079
1080 data = {
1081 "name": volume_name,
1082 }
1083
1084 value, unit = size[:-1], size[-1]
1085 bit_size = str(int(value) * SIZE[unit])
1086 LOG.info(bit_size)
1087
1088 data["size"] = bit_size
1089
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001090 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001091 system_id = get_machine(hostname)["system_id"]
1092 LOG.info(system_id)
1093
1094 volume_group_id = str(_get_volume_group_id_by_name(hostname, volume_group))
1095
1096 LOG.info(volume_group_id)
1097
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001098 # TODO validation
1099 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/volume-group/{1}/".format(
1100 system_id, volume_group_id), "create_logical_volume", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001101 LOG.info(json_res)
1102
1103 if fs_type != None or mount != None:
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001104 ret = create_volume_filesystem(
Serhii Lystopad6f2fca92020-05-17 19:32:01 +03001105 hostname, volume_group + "-" + volume_name, fs_type, mount, mount_options)
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001106
1107 return True
1108
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001109
azvyagintsevbca1f462018-05-25 19:06:46 +03001110def delete_volume(hostname, volume_name, volume_group):
1111 """
1112 Delete volume from volume group.
1113 Tips: maas always use 'volume_group-volume_name' name schema.Example: 'vg0-glusterfs'
1114 This function expexts same format.
1115
1116 CLI Example:
1117
1118 .. code-block:: bash
1119
1120 salt 'maas-node' maasng.delete_volume server_hostname volume_name volume_group
1121 salt 'maas-node' maasng.delete_volume server_hostname vg0-vol0 vg0
1122 salt-call maasng.delete_volume server_hostname volume_name volume_group
1123 """
1124
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001125 maas = _create_maas_client()
azvyagintsevbca1f462018-05-25 19:06:46 +03001126 system_id = get_machine(hostname)["system_id"]
1127 LOG.debug('delete_volume:{}'.format(system_id))
1128
1129 volume_group_id = str(_get_volume_group_id_by_name(hostname, volume_group))
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001130 volume_id = str(_get_volume_id_by_name(
1131 hostname, volume_name, volume_group))
azvyagintsevbca1f462018-05-25 19:06:46 +03001132
1133 if None in [volume_group_id, volume_id]:
1134 return False
1135
1136 data = {
1137 "id": volume_id,
1138 }
1139
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001140 # TODO validation
1141 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/volume-group/{1}/".format(
1142 system_id, volume_group_id), "delete_logical_volume", **data).read() or 'null')
azvyagintsevbca1f462018-05-25 19:06:46 +03001143 return True
1144
1145
1146def get_volumes(hostname, vg_name):
1147 """
1148 Get list of volumes in volume group.
1149 """
1150 volumes = {}
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001151 _volumes = list_volume_groups(
1152 hostname)[vg_name].get('logical_volumes', False)
azvyagintsevbca1f462018-05-25 19:06:46 +03001153 if _volumes:
1154 for item in _volumes:
1155 volumes[item["name"]] = item
1156 return volumes
1157
1158# END LVM
1159
1160
Serhii Lystopad6f2fca92020-05-17 19:32:01 +03001161def create_volume_filesystem(hostname, device, fs_type=None, mount=None, mount_options=None):
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001162
1163 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001164 system_id = get_machine(hostname)["system_id"]
1165
1166 blockdevices_id = _get_blockdevice_id_by_name(hostname, device)
1167 data = {}
1168 if fs_type != None:
1169 data["fstype"] = fs_type
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001170 # TODO validation
1171 json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
1172 system_id, blockdevices_id), "format", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001173 LOG.info(json_res)
1174
1175 if mount != None:
1176 data["mount_point"] = mount
Serhii Lystopad6f2fca92020-05-17 19:32:01 +03001177 if mount_options:
1178 data["mount_options"] = mount_options
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001179 # TODO validation
1180 json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
1181 system_id, blockdevices_id), "mount", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001182 LOG.info(json_res)
1183
1184 return True
1185
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001186
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001187def set_boot_disk(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +03001188 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001189 Create volume on volume group.
1190
1191 CLI Example:
1192
1193 .. code-block:: bash
1194
1195 salt 'maas-node' maasng.set_boot_disk server_hostname disk_name
1196 salt-call maasng.set_boot_disk server_hostname disk_name
azvyagintsevbca1f462018-05-25 19:06:46 +03001197 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001198 data = {}
1199 result = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001200 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001201 system_id = get_machine(hostname)["system_id"]
1202 blockdevices_id = _get_blockdevice_id_by_name(hostname, name)
1203
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001204 maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
1205 system_id, blockdevices_id), "set_boot_disk", **data).read()
azvyagintsevf3515c82018-06-26 18:59:05 +03001206 # TODO validation for error response
1207 # (disk does not exists and node does not exists)
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001208 result["new"] = "Disk {0} was set as bootable".format(name)
1209
1210 return result
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001211
azvyagintsevbca1f462018-05-25 19:06:46 +03001212# NETWORKING
1213
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001214
1215def list_fabric():
azvyagintsevbca1f462018-05-25 19:06:46 +03001216 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001217 Get list of all fabric
1218
1219 CLI Example:
1220
1221 .. code-block:: bash
1222
1223 salt 'maas-node' maasng.list_fabric
azvyagintsevbca1f462018-05-25 19:06:46 +03001224 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001225 fabrics = {}
1226 maas = _create_maas_client()
1227 json_res = json.loads(maas.get(u'api/2.0/fabrics/').read())
1228 LOG.info(json_res)
1229 for item in json_res:
1230 fabrics[item["name"]] = item
1231 return fabrics
1232
1233
azvyagintsevf3515c82018-06-26 18:59:05 +03001234def check_fabric(name):
1235 """
1236 Simple check that fabric already defined
1237 Return format:
1238 update - require update
1239 correct - fully coincides # not implemented
1240 not_exist - need's to be created
1241 """
1242
1243 ret = 'not_exist'
1244 fabrics = list_fabric()
1245 if name in fabrics.keys():
1246 LOG.debug("Requested fabrics with name:{} already exist".format(name))
1247 ret = 'update'
1248 return ret
1249
1250
1251def check_fabric_guess_with_cidr(name, cidrs):
1252 """
1253 Check, that fabric already defined OR it was autodiscovered
1254 WA to fix issue with hardcoded 'fabric-0'
1255 - Find all auto-discovered subnets by cidr
1256 - find all subnets, that SHOULD be configured to THIS subent
1257 Warning: most probably, will fail if some subnet defined
1258 to another fabric :(
1259
1260 { 'update' : ID } - require update
1261 { 'correct' : ID } - fully coincides # not implemented
1262 { 'not_exist' : None } - need's to be created
1263
1264 CLI Example:
1265
1266 .. code-block:: bash
1267
1268 salt 'maas-node' maasng.check_fabric_guess_with_cidr name='' cidrs=[]
1269 """
1270
1271 ret = {'not_exist': None}
1272 fabrics = list_fabric()
1273 # Simple check
1274 if name in fabrics:
1275 LOG.debug("Requested fabrics with name:{} already exist".format(name))
1276 f_id = fabrics[name]['id']
1277 ret = {'update': f_id}
1278 # Cidr check
1279 # All discovered subnets by cidr
1280 d_subnets = list_subnets(sort_by='cidr')
1281 # Check, that requested cidr already in discovered.
1282 # If it is - it would mean that fabric already
1283 # exist(fabric-0,most probably) but should be renamed.
1284 # Works only for first shot ;(
1285 # due curren-single-maas logic for 'vlan-subnet' mapping.
1286 # Probably, it will fail with future MAAS releases.
1287 for cidr in cidrs:
1288 if cidr in d_subnets:
1289 f_id = d_subnets[cidr]['vlan']['fabric_id']
1290 f_name = d_subnets[cidr]['vlan']['fabric']
1291 LOG.warning("Detected cidr:{} in fabric:{}".format(cidr, f_name))
1292 LOG.warning("Guessing, that fabric "
1293 "with current name:{}\n should be "
1294 "renamed to:{}".format(f_name, name))
1295 ret = {'update': f_id}
1296 return ret
1297 return ret
1298
1299
azvyagintsevf0904ac2018-07-05 18:53:26 +03001300def create_fabric(name, description=None, fabric_id=None, update=False):
azvyagintsevbca1f462018-05-25 19:06:46 +03001301 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001302 Create new fabric.
1303
1304 CLI Example:
1305
1306 .. code-block:: bash
1307
azvyagintsevf3515c82018-06-26 18:59:05 +03001308 salt 'maas-node' maasng.create_fabric name='123'
azvyagintsevbca1f462018-05-25 19:06:46 +03001309 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001310 result = {}
1311 data = {
1312 "name": name,
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001313 "class_type": '',
1314
1315 }
azvyagintsevf3515c82018-06-26 18:59:05 +03001316 if description:
1317 data['description'] = description
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001318
1319 maas = _create_maas_client()
azvyagintsevf0904ac2018-07-05 18:53:26 +03001320 json_res = None
azvyagintsevf3515c82018-06-26 18:59:05 +03001321 try:
1322 if update:
1323 json_res = json.loads(
1324 maas.put(u"api/2.0/fabrics/{0}/".format(fabric_id),
1325 **data).read())
1326 result["new"] = "Fabric {0} created".format(json_res["name"])
1327 else:
1328 json_res = json.loads(
1329 maas.post(u"api/2.0/fabrics/", None, **data).read())
1330 result["changes"] = "Fabric {0} updated".format(json_res["name"])
1331 except Exception as inst:
1332 LOG.debug("create_fabric data:{}".format(data))
azvyagintsev06b74932019-04-03 15:27:31 +03001333 m = inst.message
azvyagintsevf3515c82018-06-26 18:59:05 +03001334 LOG.error("Message:{0}".format(m))
1335 result['result'] = False
1336 result['comment'] = 'Error creating fabric: {0}'.format(name)
1337 result['error'] = m
1338 return result
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001339 LOG.debug("crete_fabric:{}".format(json_res))
azvyagintsevf3515c82018-06-26 18:59:05 +03001340 result['result'] = True
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001341 return result
1342
1343
azvyagintsevf3515c82018-06-26 18:59:05 +03001344def list_subnets(sort_by='name'):
azvyagintsevbca1f462018-05-25 19:06:46 +03001345 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001346 Get list of subnets from maas server
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001347
1348 CLI Example:
1349
1350 .. code-block:: bash
1351
azvyagintsevf3515c82018-06-26 18:59:05 +03001352 salt 'maas-node' maasng.list_subnets
azvyagintsevbca1f462018-05-25 19:06:46 +03001353 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001354 subnets = {}
1355 maas = _create_maas_client()
1356 json_res = json.loads(maas.get(u'api/2.0/subnets/').read())
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001357 for item in json_res:
azvyagintsevf3515c82018-06-26 18:59:05 +03001358 subnets[item[sort_by]] = item
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001359 return subnets
1360
1361
azvyagintsevf3515c82018-06-26 18:59:05 +03001362def list_vlans(fabric, sort_by='vid'):
azvyagintsevbca1f462018-05-25 19:06:46 +03001363 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001364 Get list of vlans in fabric
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001365
1366 CLI Example:
1367
1368 .. code-block:: bash
1369
azvyagintsevf3515c82018-06-26 18:59:05 +03001370 salt 'maas-node' maasng.list_vlans fabric_name
azvyagintsevbca1f462018-05-25 19:06:46 +03001371 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001372 vlans = {}
1373 maas = _create_maas_client()
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001374 fabric_id = get_fabricid(fabric)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001375
azvyagintsevf3515c82018-06-26 18:59:05 +03001376 try:
1377 json_res = json.loads(
1378 maas.get(u'api/2.0/fabrics/{0}/vlans/'.format(fabric_id)).read())
1379 except Exception as inst:
azvyagintsev06b74932019-04-03 15:27:31 +03001380 m = inst.message
azvyagintsevf3515c82018-06-26 18:59:05 +03001381 LOG.error("Message:{0}".format(m))
1382 LOG.debug(json_res)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001383 for item in json_res:
azvyagintsevf3515c82018-06-26 18:59:05 +03001384 vlans[item[sort_by]] = item
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001385 return vlans
1386
1387
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001388def get_fabricid(fabric):
azvyagintsevbca1f462018-05-25 19:06:46 +03001389 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001390 Get id for specific fabric
1391
1392 CLI Example:
1393
1394 .. code-block:: bash
1395
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001396 salt 'maas-node' maasng.get_fabricid fabric_name
azvyagintsevbca1f462018-05-25 19:06:46 +03001397 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001398 try:
1399 return list_fabric()[fabric]['id']
1400 except KeyError:
azvyagintsevefb6f5d2018-07-10 14:16:19 +03001401 return {"error": "Fabric not found on MaaS server"}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001402
1403
azvyagintsevf3515c82018-06-26 18:59:05 +03001404def check_vlan_in_fabric(fabric, vlan):
1405 """
1406 Check that VLAN exactly defined
1407 Return format:
1408 update - require update
1409 correct - fully coincides # not implemented
1410 not_exist - need's to be created
1411 """
1412
1413 ret = 'not_exist'
1414 vlans = list_vlans(fabric)
1415 if vlan in vlans.keys():
1416 LOG.debug("Requested VLAN:{} already exist"
1417 "in FABRIC:{}".format(vlan, fabric))
1418 ret = 'update'
1419 return ret
1420
1421
Petr Ruzicka80471852018-07-13 14:08:27 +02001422def create_vlan_in_fabric(name, fabric, vlan, description, primary_rack, mtu=1500,
Ivan Berezovskiy499b2502019-10-07 16:31:32 +04001423 dhcp_on=False, update=False, vlan_id="", relay_vlan=None):
azvyagintsevbca1f462018-05-25 19:06:46 +03001424 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001425 Update vlan
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001426 CLI Example:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001427 .. code-block:: bash
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001428 salt 'maas-node' maasng.update_vlan name, fabric, vid, description, dhcp_on
azvyagintsevbca1f462018-05-25 19:06:46 +03001429 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001430 result = {}
1431
1432 data = {
1433 "name": name,
1434 "dhcp_on": str(dhcp_on),
1435 "description": description,
azvyagintsev6913e5e2018-07-05 11:42:53 +03001436 "primary_rack": list_racks()[primary_rack]['system_id'],
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001437 }
Michael Polenchukd25da792018-07-19 18:27:11 +04001438 if mtu:
1439 data['mtu'] = str(mtu)
Ivan Berezovskiy499b2502019-10-07 16:31:32 +04001440 if relay_vlan:
1441 data['relay_vlan'] = str(relay_vlan)
azvyagintsevf3515c82018-06-26 18:59:05 +03001442 vlan = str(vlan)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001443 maas = _create_maas_client()
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001444 fabric_id = get_fabricid(fabric)
azvyagintsevf3515c82018-06-26 18:59:05 +03001445 try:
1446 if update:
1447 # MAAS have buggy logic here. Fallowing api reference, here should
1448 # be passed VID - which mean, API ID for vlan.
1449 # Otherwise, at least for maas 2.3.3-6498-ge4db91d exactly VLAN
1450 # should be passed. so, make temp.backward-convertation.
1451 # json_res = json.loads(maas.put(u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id,vlan_id), **data).read())
1452 json_res = json.loads(maas.put(
1453 u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id, vlan),
1454 **data).read())
1455 else:
1456 data['vid'] = str(vlan)
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001457 json_res = json.loads(maas.post(
1458 u'api/2.0/fabrics/{0}/vlans/'.format(fabric_id), None, **data).read())
azvyagintsevf3515c82018-06-26 18:59:05 +03001459 except Exception as inst:
1460 LOG.debug("create_vlan_in_fabric data:{}".format(data))
azvyagintsev06b74932019-04-03 15:27:31 +03001461 m = inst.message
azvyagintsevf3515c82018-06-26 18:59:05 +03001462 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))
azvyagintsev06b74932019-04-03 15:27:31 +03001525 m = inst.message
azvyagintsevf3515c82018-06-26 18:59:05 +03001526 LOG.error("Message:{0}".format(m))
1527 result['result'] = False
1528 result['comment'] = 'Error creating subnet: {0}'.format(name)
1529 result['error'] = m
1530 return result
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001531 LOG.debug("create_subnet:{}".format(json_res))
azvyagintsevf3515c82018-06-26 18:59:05 +03001532 result["new"] = "Subnet {0} with CIDR {1}" \
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001533 "and gateway {2} was created".format(
1534 name, cidr, gateway_ip)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001535
1536 return result
1537
1538
1539def get_subnet(subnet):
1540 """
1541 Get details for specific subnet
1542
1543 CLI Example:
1544
1545 .. code-block:: bash
1546
1547 salt 'maas-node' maasng.get_subnet subnet_name
1548 """
1549 try:
azvyagintsevf3515c82018-06-26 18:59:05 +03001550 return list_subnets()[subnet]
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001551 except KeyError:
1552 return {"error": "Subnet not found on MaaS server"}
1553
1554
1555def get_subnetid(subnet):
1556 """
1557 Get id for specific subnet
1558
1559 CLI Example:
1560
1561 .. code-block:: bash
1562
1563 salt 'maas-node' maasng.get_subnetid subnet_name
1564 """
1565 try:
azvyagintsevf3515c82018-06-26 18:59:05 +03001566 return list_subnets()[subnet]['id']
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001567 except KeyError:
1568 return {"error": "Subnet not found on MaaS server"}
1569
1570
1571def list_ipranges():
1572 """
1573 Get list of all ipranges from maas server
1574
1575 CLI Example:
1576
1577 .. code-block:: bash
1578
1579 salt 'maas-node' maasng.list_ipranges
1580 """
1581 ipranges = {}
1582 maas = _create_maas_client()
1583 json_res = json.loads(maas.get(u'api/2.0/ipranges/').read())
1584 for item in json_res:
1585 ipranges[item["start_ip"]] = item
1586 return ipranges
1587
1588
azvyagintsevf0904ac2018-07-05 18:53:26 +03001589def create_iprange(type_range, start_ip, end_ip, subnet=None, comment=None):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001590 """
1591 Create ip range
1592
1593 CLI Example:
1594
1595 .. code-block:: bash
1596
1597 salt 'maas-node' maasng.create_iprange type, start ip, end ip, comment
1598 """
1599 result = {}
1600
1601 data = {
1602 "type": type_range,
1603 "start_ip": start_ip,
1604 "end_ip": end_ip,
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001605 }
azvyagintsevf3515c82018-06-26 18:59:05 +03001606 if comment:
1607 data['comment'] = comment
azvyagintsevf0904ac2018-07-05 18:53:26 +03001608 if subnet:
1609 subnet_id = list_subnets()[subnet]['id']
1610 data['subnet'] = str(subnet_id)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001611 maas = _create_maas_client()
azvyagintsevf3515c82018-06-26 18:59:05 +03001612 _name = "Type:{}: {}-{}".format(type_range, start_ip, end_ip)
1613 try:
1614 json_res = json.loads(
1615 maas.post(u"api/2.0/ipranges/", None, **data).read())
1616 except Exception as inst:
azvyagintsev06b74932019-04-03 15:27:31 +03001617 m = inst.message
azvyagintsevf3515c82018-06-26 18:59:05 +03001618 LOG.error("Message:{0}".format(m))
1619 result['result'] = False
1620 result['comment'] = 'Error creating iprange:{0}'.format(_name)
1621 result['error'] = m
1622 return result
1623 result["new"] = "Iprange: {0} has been created".format(_name)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001624 LOG.debug("create_iprange:{}".format(json_res))
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001625
1626 return result
1627
1628
1629def get_iprangeid(start_ip):
1630 """
1631 Get id for ip range from maas server
1632
1633 CLI Example:
1634
1635 .. code-block:: bash
1636
1637 salt 'maas-node' maasng.get_iprangeid start_ip
1638 """
1639 try:
1640 return list_ipranges()[start_ip]['id']
1641 except KeyError:
1642 return {"error": "Ip range not found on MaaS server"}
1643
1644
1645def get_startip(start_ip):
1646 """
1647 Get start ip for ip range
1648
1649 CLI Example:
1650
1651 .. code-block:: bash
1652
1653 salt 'maas-node' maasng.get_startip start ip
1654 """
1655 try:
1656 return list_ipranges()[start_ip]
1657 except KeyError:
1658 return {"error": "Ip range not found on MaaS server"}
azvyagintsevbca1f462018-05-25 19:06:46 +03001659# END NETWORKING
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001660
1661# MAAS CONFIG SECTION
1662
1663
azvyagintsev58947072018-06-29 12:09:48 +03001664def _getHTTPCode(url):
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001665 code = '003'
1666 m = ''
azvyagintsev58947072018-06-29 12:09:48 +03001667 try:
1668 connection = urllib2.urlopen(url)
1669 code = connection.getcode()
1670 connection.close()
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001671 except (urllib2.HTTPError, urllib2.URLError) as e:
1672 try:
1673 code = e.getcode()
1674 except:
1675 m = e.reason
1676 pass
azvyagintsevf3515c82018-06-26 18:59:05 +03001677 LOG.debug("Unexpected http code:{} from "
1678 "url:{}\nwith message:{}".format(code, url, m))
azvyagintsev58947072018-06-29 12:09:48 +03001679 pass
1680 return code
1681
1682
1683def wait_for_http_code(url=None, expected=[200]):
1684 """
1685 Simple function, which just wait for avaible api, aka wait for 200.
1686
1687 CLI Example:
1688
1689 .. code-block:: bash
1690
1691 salt 'maas-node' maasng.wait_for_http_code url expected=[200]
1692
1693 """
1694 ret = {}
1695 started_at = time.time()
1696 poll_time = 5
1697 timeout = 60 * 2
1698 while _getHTTPCode(url) not in expected:
1699 c_timeout = timeout - (time.time() - started_at)
1700 if c_timeout <= 0:
1701 ret['result'] = False
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001702 ret["comment"] = "api:{} not answered in time".format(url)
azvyagintsev58947072018-06-29 12:09:48 +03001703 return ret
1704 LOG.info(
1705 "Waiting for api:{0}\n"
1706 "sleep for:{1}s "
1707 "Left:{2}/{3}s".format(url, poll_time, round(c_timeout),
1708 timeout))
1709 time.sleep(poll_time)
1710 ret['result'] = True
1711 ret["comment"] = "MAAS API:{} up.".format(url)
1712 return ret
1713
1714
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001715def _get_boot_source_id_by_url(url):
1716 # FIXME: fix ret\validation
1717 try:
1718 bs_id = get_boot_source(url=url)["id"]
1719 except KeyError:
1720 return {"error": "boot-source:{0} not exist!".format(url)}
1721 return bs_id
1722
1723
1724def get_boot_source(url=None):
1725 """
1726 Read a boot source by url. If url not specified - return all.
1727
1728 CLI Example:
1729
1730 .. code-block:: bash
1731
1732 salt 'maas-node' maasng.get_boot_source url
1733
1734 """
1735 boot_sources = {}
1736 maas = _create_maas_client()
1737 json_res = json.loads(maas.get(u'api/2.0/boot-sources/').read() or 'null')
1738 for item in json_res:
1739 boot_sources[str(item["url"])] = item
1740 if url:
1741 return boot_sources.get(url, {})
1742 return boot_sources
1743
1744
1745def delete_boot_source(url, bs_id=None):
1746 """
1747 Delete a boot source by url.
1748
1749 CLI Example:
1750
1751 .. code-block:: bash
1752
1753 sal 'maas-node' maasng.delete url
1754
1755 """
1756 result = {}
1757 if not bs_id:
1758 bs_id = _get_boot_source_id_by_url(url)
1759 maas = _create_maas_client()
1760 json_res = json.loads(maas.delete(
1761 u'/api/2.0/boot-sources/{0}/'.format(bs_id)).read() or 'null')
1762 LOG.debug("delete_boot_source:{}".format(json_res))
1763 result["new"] = "Boot-resource {0} deleted".format(url)
1764 return result
1765
1766
1767def boot_sources_delete_all_others(except_urls=[]):
1768 """
1769 Delete all boot-sources, except defined in 'except_urls' list.
1770 """
azvyagintseve2e37a12018-11-01 14:45:49 +02001771 result = {'changes': {}}
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001772 maas_boot_sources = get_boot_source()
1773 if 0 in [len(except_urls), len(maas_boot_sources)]:
1774 result['result'] = None
azvyagintseve2e37a12018-11-01 14:45:49 +02001775 result["comment"] = "'except_urls' or 'maas boot-sources' for " \
1776 "delete empty. No changes goinng to be."
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001777 return result
azvyagintseve2e37a12018-11-01 14:45:49 +02001778 # FIXME: fix 'changes' accumulator
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001779 for url in maas_boot_sources.keys():
1780 if url not in except_urls:
1781 LOG.info("Removing boot-source:{}".format(url))
1782 boot_resources_import(action='stop_import', wait=True)
1783 result["changes"] = delete_boot_source(url)
1784 return result
1785
1786
1787def create_boot_source(url, keyring_filename='', keyring_data='', wait=False):
1788 """
1789 Create and import maas boot-source: link to maas-ephemeral repo
1790 Be aware, those step will import resource to rack ctrl, but you also need to import
1791 them into the region!
1792
1793
1794 :param url: The URL of the BootSource.
1795 :param keyring_filename: The path to the keyring file for this BootSource.
1796 :param keyring_data: The GPG keyring for this BootSource, base64-encoded data.
1797
1798 """
1799
1800 # TODO: not work with 'update' currently => keyring update may fail.
1801 result = {}
1802
1803 data = {
1804 "url": url,
1805 "keyring_filename": keyring_filename,
1806 "keyring_data": str(keyring_data),
1807 }
1808
1809 maas = _create_maas_client()
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001810 if url in get_boot_source():
1811 result['result'] = None
1812 result["comment"] = "boot resource already exist"
1813 return result
1814
1815 # NOTE: maas.post will return 400, if url already defined.
1816 json_res = json.loads(
1817 maas.post(u'api/2.0/boot-sources/', None, **data).read())
1818 if wait:
1819 LOG.debug(
1820 "Sleep for 5s,to get MaaS some time to process previous request")
1821 time.sleep(5)
1822 ret = boot_resources_is_importing(wait=True)
1823 if ret is dict:
1824 return ret
1825 LOG.debug("create_boot_source:{}".format(json_res))
1826 result["new"] = "boot resource {0} was created".format(json_res["url"])
1827
1828 return result
1829
1830
1831def boot_resources_import(action='import', wait=False):
1832 """
1833 import/stop_import the boot resources.
1834
1835 :param action: import\stop_import
1836 :param wait: True\False. Wait till process finished.
1837
1838 CLI Example:
1839
1840 .. code-block:: bash
1841
1842 salt 'maas-node' maasng.boot_resources_import action='import'
1843
1844 """
1845 maas = _create_maas_client()
1846 # Have no idea why, but usual jsonloads not work here..
1847 imp = maas.post(u'api/2.0/boot-resources/', action)
1848 if imp.code == 200:
1849 LOG.debug('boot_resources_import:{}'.format(imp.readline()))
1850 if wait:
1851 boot_resources_is_importing(wait=True)
1852 return True
1853 else:
1854 return False
1855
1856
1857def boot_resources_is_importing(wait=False):
1858 maas = _create_maas_client()
1859 result = {}
1860 if wait:
1861 started_at = time.time()
1862 poll_time = 5
1863 timeout = 60 * 15
1864 while boot_resources_is_importing(wait=False):
1865 c_timeout = timeout - (time.time() - started_at)
1866 if c_timeout <= 0:
1867 result['result'] = False
1868 result["comment"] = "Boot-resources import not finished in time"
1869 return result
1870 LOG.info(
1871 "Waiting boot-resources import done\n"
1872 "sleep for:{}s "
1873 "Left:{}/{}s".format(poll_time, round(c_timeout), timeout))
1874 time.sleep(poll_time)
1875 return json.loads(
1876 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1877 else:
1878 return json.loads(
1879 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1880
1881#####
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001882# def boot_sources_selections_delete_all_others(except_urls=[]):
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001883# """
1884# """
1885# result = {}
1886# return result
1887
1888
1889def is_boot_source_selections_in(dict1, list1):
1890 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001891 Check that requested boot-selection already in maas bs selections,
1892 if True- return bss id.
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001893 # FIXME: those hack check doesn't look good.
1894 """
1895 for bs in list1:
1896 same = set(dict1.keys()) & set(bs.keys())
1897 if all(elem in same for elem in
1898 ['os', 'release', 'arches', 'subarches', 'labels']):
azvyagintsevf3515c82018-06-26 18:59:05 +03001899 LOG.debug("boot-selection in maas:{0}\n"
1900 "looks same to requested:{1}".format(bs, dict1))
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001901 return bs['id']
1902 return False
1903
1904
1905def get_boot_source_selections(bs_url):
1906 """
1907 Get boot-source selections.
1908 """
1909 # check for key_error!
1910 bs_id = _get_boot_source_id_by_url(bs_url)
1911 maas = _create_maas_client()
1912 json_res = json.loads(
1913 maas.get(u'/api/2.0/boot-sources/{0}/selections/'.format(bs_id)).read())
1914 LOG.debug(
1915 "get_boot_source_selections for url:{} \n{}".format(bs_url, json_res))
1916 return json_res
1917
1918
1919def create_boot_source_selections(bs_url, os, release, arches="*",
1920 subarches="*", labels="*", wait=True):
1921 """
1922 Create a new boot source selection for bs_url.
1923 :param os: The OS (e.g. ubuntu, centos) for which to import resources.Required.
1924 :param release: The release for which to import resources. Required.
1925 :param arches: The architecture list for which to import resources.
1926 :param subarches: The subarchitecture list for which to import resources.
1927 :param labels: The label lists for which to import resources.
1928 """
1929
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001930 result = {"result": True, 'name': bs_url, 'changes': None}
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001931
1932 data = {
1933 "os": os,
1934 "release": release,
1935 "arches": arches,
1936 "subarches": subarches,
1937 "labels": labels,
1938 }
1939
1940 maas = _create_maas_client()
1941 bs_id = _get_boot_source_id_by_url(bs_url)
1942 # TODO add pre-create verify
1943 maas_bs_s = get_boot_source_selections(bs_url)
1944 if is_boot_source_selections_in(data, maas_bs_s):
1945 result["result"] = True
azvyagintsevf3515c82018-06-26 18:59:05 +03001946 result["comment"] = 'Requested boot-source selection ' \
1947 'for {0} already exist.'.format(
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001948 bs_url)
1949 return result
1950
1951 # NOTE: maas.post will return 400, if url already defined.
azvyagintsevcb54d142018-06-19 16:18:32 +03001952 # Also, maas need's some time to import info about stream.
1953 # unfortunatly, maas don't have any call to check stream-import-info - so, we need to implement
1954 # at least simple retry ;(
1955 json_res = False
1956 poll_time = 5
azvyagintseve2e37a12018-11-01 14:45:49 +02001957 for i in range(0, 10):
azvyagintsevcb54d142018-06-19 16:18:32 +03001958 try:
1959 json_res = json.loads(
1960 maas.post(u'api/2.0/boot-sources/{0}/selections/'.format(bs_id), None,
1961 **data).read())
1962 except Exception as inst:
azvyagintsev06b74932019-04-03 15:27:31 +03001963 m = inst.message
azvyagintsevf3515c82018-06-26 18:59:05 +03001964 LOG.warning("boot_source_selections "
1965 "catch error during processing. Most-probably, "
azvyagintseve2e37a12018-11-01 14:45:49 +02001966 "streams data not imported yet.\nSleep:{}s "
1967 "Retry:{}/10".format(poll_time, i))
azvyagintsevcb54d142018-06-19 16:18:32 +03001968 LOG.warning("Message:{0}".format(m))
1969 time.sleep(poll_time)
1970 continue
1971 break
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001972 LOG.debug("create_boot_source_selections:{}".format(json_res))
azvyagintsevcb54d142018-06-19 16:18:32 +03001973 if not json_res:
1974 result["result"] = False
azvyagintsevf3515c82018-06-26 18:59:05 +03001975 result["comment"] = 'Failed to create requested boot-source selection' \
1976 ' for {0}.'.format(bs_url)
azvyagintsevcb54d142018-06-19 16:18:32 +03001977 return result
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001978 if wait:
1979 LOG.debug(
1980 "Sleep for 5s,to get MaaS some time to process previous request")
1981 time.sleep(5)
1982 ret = boot_resources_import(action='import', wait=True)
1983 if ret is dict:
1984 return ret
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001985 result["comment"] = "boot-source selection for {0} was created".format(
1986 bs_url)
azvyagintsevcb54d142018-06-19 16:18:32 +03001987 result["new"] = data
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001988
1989 return result
1990
1991# END MAAS CONFIG SECTION
azvyagintsevcb54d142018-06-19 16:18:32 +03001992
1993# RACK CONTROLLERS SECTION
1994
1995
1996def get_rack(hostname):
1997 """
1998 Get information about specified rackd
1999
2000 CLI Example:
2001
2002 .. code-block:: bash
2003
2004 salt-call maasng.get_rack rack_hostname
2005 """
2006 try:
2007 return list_racks()[hostname]
2008 except KeyError:
2009 return {"error": "rack:{} not found on MaaS server".format(hostname)}
2010
2011
azvyagintsev6913e5e2018-07-05 11:42:53 +03002012def list_racks(sort_by='hostname'):
azvyagintsevcb54d142018-06-19 16:18:32 +03002013 """
2014 Get list of all rack controllers from maas server
2015
2016 CLI Example:
2017
2018 .. code-block:: bash
2019
2020 salt-call maasng.list_racks
2021 """
2022 racks = {}
2023 maas = _create_maas_client()
2024 json_res = json.loads(
2025 maas.get(u"/api/2.0/rackcontrollers/").read() or 'null')
2026 for item in json_res:
azvyagintsev6913e5e2018-07-05 11:42:53 +03002027 racks[item[sort_by]] = item
azvyagintsevcb54d142018-06-19 16:18:32 +03002028 return racks
2029
2030
2031def sync_bs_to_rack(hostname=None):
2032 """
2033 Sync RACK boot-sources with REGION. If no hostname probided - sync to all.
2034
2035 CLI Example:
2036
2037 .. code-block:: bash
2038
2039 salt-call maasng.sync_bs_to_rack rack_hostname
2040 """
2041 ret = {}
2042 maas = _create_maas_client()
2043 if not hostname:
2044 LOG.info("boot-sources sync initiated for ALL Rack's")
2045 # Convert to json-like format
2046 json_res = json.loads('["{0}"]'.format(
2047 maas.post(u"/api/2.0/rackcontrollers/",
2048 'import_boot_images').read()))
2049 LOG.debug("sync_bs_to_rack:{}".format(json_res))
2050 ret['result'] = True
2051 ret['comment'] = "boot-sources sync initiated for ALL Rack's"
2052 return ret
2053 LOG.info("boot-sources sync initiated for RACK:{0}".format(hostname))
2054 # Convert to json-like format
2055 json_res = json.loads('["{0}"]'.format(maas.post(
2056 u"/api/2.0/rackcontrollers/{0}/".format(
2057 get_rack(hostname)['system_id']),
2058 'import_boot_images').read()))
2059 LOG.debug("sync_bs_to_rack:{}".format(json_res))
2060 ret['result'] = True
2061 ret['comment'] = "boot-sources sync initiated for {0} Rack's".format(
2062 hostname)
2063 return
2064
2065
2066def rack_list_boot_imgs(hostname):
2067 ret = {}
2068 maas = _create_maas_client()
2069 LOG.debug("rack_list_boot_imgs:{}".format(hostname))
2070 ret = json.loads(maas.get(u"/api/2.0/rackcontrollers/{0}/".format(
2071 get_rack(hostname)['system_id']), 'list_boot_images').read() or 'null')
2072 return ret
2073
2074
2075def is_rack_synced(hostname):
2076 rez = rack_list_boot_imgs(hostname)['status']
2077 if rez == 'synced':
2078 return True
2079 return False
2080
2081# TODO do we actually need _exact_ check per-pack?
2082# def wait_for_images_on_rack(hostname):
2083#
2084# """
2085# WA function, to be able check that RACK actually done SYNC images
2086# for REQUIRED images at least.
2087# Required image to be fetched from
2088# reclass:maas:region:boot_sources_selections:[keys]:os/release formation
2089#
2090# CLI Example:
2091#
2092# .. code-block:: bash
2093#
2094# salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
2095# """
2096# try:
2097# bss = __salt__['config.get']('maas')['region']['boot_sources_selections']
2098# except KeyError:
2099# ret['result'] = None
2100# ret['comment'] = "boot_sources_selections definition for sync not found."
2101# return ret
2102# s_names = []
2103# # Format u'name': u'ubuntu/xenial'
2104# for v in bss.values():s_names.append("{0}/{1}".format(v['os'],v['release']))
2105# # Each names, should be in rack and whole rack should be in sync-ed state
2106
2107
2108def sync_and_wait_bs_to_all_racks():
2109 """
2110 Sync ALL rack's with regions source images.
2111
2112 CLI Example:
2113
2114 .. code-block:: bash
2115
2116 salt-call maasng.sync_and_wait_bs_to_all_racks
2117 """
2118 sync_bs_to_rack()
2119 for rack in list_racks().keys():
2120 wait_for_sync_bs_to_rack(hostname=rack)
2121 return True
2122
2123
2124def wait_for_sync_bs_to_rack(hostname=None):
2125 """
2126 Wait for boot images sync finished, on exact rack
2127
2128 CLI Example:
2129
2130 .. code-block:: bash
2131
2132 salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
2133 """
2134 ret = {}
2135 started_at = time.time()
2136 poll_time = 5
2137 timeout = 60 * 15
2138 while not is_rack_synced(hostname):
2139 c_timeout = timeout - (time.time() - started_at)
2140 if c_timeout <= 0:
2141 ret['result'] = False
2142 ret[
2143 "comment"] = "Boot-resources sync on rackd:{0}" \
2144 "not finished in time".format(
2145 hostname)
2146 return ret
2147 LOG.info(
2148 "Waiting boot-resources sync done to rack:{0}\n"
2149 "sleep for:{1}s "
2150 "Left:{2}/{3}s".format(hostname, poll_time, round(c_timeout),
2151 timeout))
2152 time.sleep(poll_time)
2153 ret['result'] = is_rack_synced(hostname)
2154 ret["comment"] = "Boot-resources sync on rackd:{0} finished".format(
2155 hostname)
2156 return ret
2157
2158# END RACK CONTROLLERS SECTION
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02002159# SSHKEYS
2160
2161
2162def list_sshkeys():
2163 """
2164 Get list of all sshkeys
2165
2166 CLI Example:
2167
2168 .. code-block:: bash
2169
2170 salt 'maas-node' maasng.list_sshkeys
2171 salt-call maasng.list_sshkeys
2172 """
2173 ssh = {}
2174 maas = _create_maas_client()
2175 json_res = json.loads(maas.get(u'api/2.0/account/prefs/sshkeys/').read())
2176 LOG.info(json_res)
2177 for item in json_res:
2178 ssh[item["key"]] = item
2179 return ssh
2180
2181
2182def add_sshkey(sshkey):
2183 """
2184 Add SSH key for user to MAAS.
2185
2186 CLI Example:
2187
2188 .. code-block:: bash
2189
2190 salt 'maas-node' maasng.add_sshkey sshkey
2191 salt-call maasng.add_sshkey sshkey
2192 """
2193 data = {
2194 "key": sshkey,
2195 }
2196 result = {}
2197 maas = _create_maas_client()
2198
2199 maas.post(u"/api/2.0/account/prefs/sshkeys/", None, **data).read()
2200 result["new"] = "SSH Key {0} was added.".format(sshkey)
2201
2202 return result
2203
2204
2205def get_sshkey(sshkey):
2206 """
2207 Get start ip for ip range
2208
2209 CLI Example:
2210
2211 .. code-block:: bash
2212
2213 salt 'maas-node' maasng.get_sshkey sshkey
2214 salt-call maasng.get_sshkey sshkey
2215 """
2216 try:
2217 return list_sshkeys()[sshkey]
2218 except KeyError:
2219 return {"error": "SSH key not found on MaaS server"}
2220# END SSHKEYS