blob: d159c9c1a511c28b2399a665dc6a2cb06d0eb26d [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
22import os.path
23import time
24import urllib2
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020025# Salt utils
Ondrej Smolab57a23b2018-01-24 11:18:24 +010026from salt.exceptions import CommandExecutionError, SaltInvocationError
27
28LOG = logging.getLogger(__name__)
29
30SIZE = {
31 "M": 1000000,
32 "G": 1000000000,
33 "T": 1000000000000,
34}
35
36RAID = {
37 0: "raid-0",
38 1: "raid-1",
39 5: "raid-5",
40 10: "raid-10",
41}
42
43# Import third party libs
44HAS_MASS = False
45try:
46 from maas_client import MAASClient, MAASDispatcher, MAASOAuth
47 HAS_MASS = True
48except ImportError:
49 LOG.debug('Missing MaaS client module is Missing. Skipping')
50
51
52def __virtual__():
azvyagintsevbca1f462018-05-25 19:06:46 +030053 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +010054 Only load this module if maas-client
55 is installed on this minion.
azvyagintsevbca1f462018-05-25 19:06:46 +030056 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +010057 if HAS_MASS:
58 return 'maasng'
59 return False
60
61
62APIKEY_FILE = '/var/lib/maas/.maas_credentials'
63
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020064
Ondrej Smolab57a23b2018-01-24 11:18:24 +010065def _format_data(data):
66 class Lazy:
67 def __str__(self):
68 return ' '.join(['{0}={1}'.format(k, v)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020069 for k, v in data.iteritems()])
Ondrej Smolab57a23b2018-01-24 11:18:24 +010070 return Lazy()
71
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020072
Ondrej Smolab57a23b2018-01-24 11:18:24 +010073def _create_maas_client():
74 global APIKEY_FILE
75 try:
76 api_token = file(APIKEY_FILE).read().splitlines()[-1].strip()\
77 .split(':')
78 except:
79 LOG.exception('token')
80 auth = MAASOAuth(*api_token)
81 api_url = 'http://localhost:5240/MAAS'
82 dispatcher = MAASDispatcher()
83 return MAASClient(auth, dispatcher, api_url)
84
Ondrej Smolab57a23b2018-01-24 11:18:24 +010085
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020086def _get_blockdevice_id_by_name(hostname, device):
87
88 # TODO validation
Ondrej Smolab57a23b2018-01-24 11:18:24 +010089 return list_blockdevices(hostname)[device]["id"]
90
Ondrej Smolab57a23b2018-01-24 11:18:24 +010091
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020092def _get_volume_group_id_by_name(hostname, device):
93
94 # TODO validation
Ondrej Smolab57a23b2018-01-24 11:18:24 +010095 return list_volume_groups(hostname)[device]["id"]
96
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020097
azvyagintsevbca1f462018-05-25 19:06:46 +030098def _get_volume_id_by_name(hostname, volume_name, volume_group, maas_volname=True):
99
100 if not maas_volname:
101 # MAAS-like name
102 volume_name = str("%s-%s" % (volume_group,volume_name))
103 ##TODO validation
104 return get_volumes(hostname, volume_group)[volume_name]["id"]
105
106
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100107def _get_partition_id_by_name(hostname, device, partition):
108
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200109 # TODO validation
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100110 return list_partitions(hostname, device)[partition]["id"]
111
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200112# MACHINE SECTION
113
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100114
115def get_machine(hostname):
azvyagintsevbca1f462018-05-25 19:06:46 +0300116 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100117 Get information aboout specified machine
118
119 CLI Example:
120
121 .. code-block:: bash
122
123 salt-call maasng.get_machine server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300124 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100125 try:
126 return list_machines()[hostname]
127 except KeyError:
128 return {"error": "Machine not found on MaaS server"}
129
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200130
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100131def list_machines():
azvyagintsevbca1f462018-05-25 19:06:46 +0300132 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100133 Get list of all machines from maas server
134
135 CLI Example:
136
137 .. code-block:: bash
138
139 salt 'maas-node' maasng.list_machines
azvyagintsevbca1f462018-05-25 19:06:46 +0300140 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200141 machines = {}
142 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100143 json_res = json.loads(maas.get(u'api/2.0/machines/').read())
144 for item in json_res:
145 machines[item["hostname"]] = item
146 return machines
147
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200148
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100149def create_machine():
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200150 # TODO
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100151
152 return False
153
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200154
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100155def update_machine():
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200156 # TODO
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100157
158 return False
159
azvyagintsevbca1f462018-05-25 19:06:46 +0300160# END MACHINE SECTION
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200161# RAID SECTION
162
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100163
164def create_raid(hostname, name, level, disks=[], partitions=[], **kwargs):
azvyagintsevbca1f462018-05-25 19:06:46 +0300165 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100166 Create new raid on machine.
167
168 CLI Example:
169
170 .. code-block:: bash
171
172 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 +0300173 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100174
175 result = {}
176
177 if len(disks) == 0 and len(partitions) == 0:
178 result["error"] = "Disks or partitions need to be provided"
179
180 disk_ids = []
181 partition_ids = []
182
183 for disk in disks:
184 try:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200185 disk_ids.append(str(_get_blockdevice_id_by_name(hostname, disk)))
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100186 except KeyError:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200187 result["error"] = "Device {0} does not exists on machine {1}".format(
188 disk, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100189 return result
190
191 for partition in partitions:
192 try:
193 device = partition.split("-")[0]
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200194 device_part = list_partitions(hostname, device)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100195 partition_ids.append(str(device_part[partition]["id"]))
196 except KeyError:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200197 result["error"] = "Partition {0} does not exists on machine {1}".format(
198 partition, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100199 return result
200
201 data = {
202 "name": name,
203 "level": RAID[int(level)],
204 "block_devices": disk_ids,
205 "partitions": partition_ids,
206 }
207
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200208 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100209 system_id = get_machine(hostname)["system_id"]
210 LOG.info(system_id)
211
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200212 # TODO validation
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100213 LOG.info(data)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200214 json_res = json.loads(
215 maas.post(u"api/2.0/nodes/{0}/raids/".format(system_id), None, **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100216 LOG.info(json_res)
217 result["new"] = "Raid {0} created".format(name)
218
219 return result
220
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200221
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100222def list_raids(hostname):
azvyagintsevbca1f462018-05-25 19:06:46 +0300223 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100224 Get list all raids on machine
225
226 CLI Example:
227
228 .. code-block:: bash
229
230 salt-call maasng.list_raids server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300231 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100232
azvyagintsevbca1f462018-05-25 19:06:46 +0300233 raids = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200234 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100235 system_id = get_machine(hostname)["system_id"]
azvyagintsevbca1f462018-05-25 19:06:46 +0300236 #TODO validation
237 json_res = json.loads(maas.get(u"api/2.0/nodes/{0}/raids/".format(system_id)).read())
238 LOG.debug('list_raids:{} {}'.format(system_id, json_res))
239 for item in json_res:
240 raids[item["name"]] = item
241 return raids
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100242
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200243
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100244def get_raid(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300245 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100246 Get information about specific raid on machine
247
248 CLI Example:
249
250 .. code-block:: bash
251
252 salt-call maasng.get_raids server_hostname md0
azvyagintsevbca1f462018-05-25 19:06:46 +0300253 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100254
255 return list_raids(hostname)[name]
256
257
azvyagintsevbca1f462018-05-25 19:06:46 +0300258def _get_raid_id_by_name(hostname, raid_name):
259 return get_raid(hostname, raid_name)['id']
260
261
262def delete_raid(hostname, raid_name):
263 """
264 Delete RAID on a machine.
265
266 CLI Example:
267
268 .. code-block:: bash
269
270 salt 'maas-node' maasng.delete_raid server_hostname raid_name
271 salt-call maasng.delete_raid server_hostname raid_name
272 """
273 result = {}
274 maas=_create_maas_client()
275 system_id = get_machine(hostname)["system_id"]
276 raid_id = _get_raid_id_by_name(hostname, raid_name)
277 LOG.debug('delete_raid: {} {}'.format(system_id,raid_id))
278 maas.delete(u"api/2.0/nodes/{0}/raid/{1}/".format(system_id, raid_id)).read()
279
280 result["new"] = "Raid {0} deleted".format(raid_name)
281 return result
282
283# END RAID SECTION
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200284# BLOCKDEVICES SECTION
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100285
azvyagintsevbca1f462018-05-25 19:06:46 +0300286
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100287def list_blockdevices(hostname):
azvyagintsevbca1f462018-05-25 19:06:46 +0300288 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100289 Get list of all blockdevices (disks) on machine
290
291 CLI Example:
292
293 .. code-block:: bash
294
295 salt 'maas-node' maasng.list_blockdevices server_hostname
296 salt-call maasng.list_blockdevices server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300297 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100298 ret = {}
299
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200300 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100301 system_id = get_machine(hostname)["system_id"]
302 LOG.info(system_id)
303
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200304 # TODO validation if exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100305
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200306 json_res = json.loads(
307 maas.get(u"api/2.0/nodes/{0}/blockdevices/".format(system_id)).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100308 LOG.info(json_res)
309 for item in json_res:
310 ret[item["name"]] = item
311
312 return ret
313
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200314
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100315def get_blockdevice(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300316 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100317 Get information about blockdevice (disk) on machine
318
319 CLI Example:
320
321 .. code-block:: bash
322
323 salt 'maas-node' maasng.get_blockdevice server_hostname sda
324 salt-call maasng.get_blockdevice server_hostname sda
azvyagintsevbca1f462018-05-25 19:06:46 +0300325 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100326
327 return list_blockdevices(hostname)[name]
328
azvyagintsevbca1f462018-05-25 19:06:46 +0300329# END BLOCKDEVICES SECTION
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200330# PARTITIONS
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100331
azvyagintsevbca1f462018-05-25 19:06:46 +0300332
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100333def list_partitions(hostname, device):
azvyagintsevbca1f462018-05-25 19:06:46 +0300334 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100335 Get list of all partitions on specific device located on specific machine
336
337 CLI Example:
338
339 .. code-block:: bash
340
341 salt 'maas-node' maasng.list_partitions server_hostname sda
342 salt-call maasng.list_partitions server_hostname sda
azvyagintsevbca1f462018-05-25 19:06:46 +0300343 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100344 ret = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200345 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100346 system_id = get_machine(hostname)["system_id"]
347 LOG.info(system_id)
348
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200349 partitions = get_blockdevice(hostname, device)["partitions"]
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100350 LOG.info(partitions)
351
352 #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 +0200353 # LOG.info(json_res)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100354
355 if len(device) > 0:
356 for item in partitions:
357 name = item["path"].split('/')[-1]
358 ret[name] = item
359
360 return ret
361
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200362
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100363def get_partition(hostname, device, partition):
azvyagintsevbca1f462018-05-25 19:06:46 +0300364 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100365 Get information about specific parition on device located on machine
366
367 CLI Example:
368
369 .. code-block:: bash
370
371 salt 'maas-node' maasng.get_partition server_hostname disk_name partition
372 salt-call maasng.get_partition server_hostname disk_name partition
373
374 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300375 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100376
377 return list_partitions(partition)[name]
378
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200379
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100380def create_partition(hostname, disk, size, fs_type=None, mount=None):
azvyagintsevbca1f462018-05-25 19:06:46 +0300381 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100382 Create new partition on device.
383
384 CLI Example:
385
386 .. code-block:: bash
387
388 salt 'maas-node' maasng.create_partition server_hostname disk_name 10 ext4 "/"
389 salt-call maasng.create_partition server_hostname disk_name 10 ext4 "/"
azvyagintsevbca1f462018-05-25 19:06:46 +0300390 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200391 # TODO validation
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100392 result = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200393 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100394 system_id = get_machine(hostname)["system_id"]
395 LOG.info(system_id)
396
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200397 device_id = _get_blockdevice_id_by_name(hostname, disk)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100398 LOG.info(device_id)
399
400 value, unit = size[:-1], size[-1]
401 calc_size = str(int(value) * SIZE[unit])
402 LOG.info(calc_size)
403
404 data = {
405 "size": calc_size
406 }
407
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200408 # TODO validation
409 partition = json.loads(maas.post(
410 u"api/2.0/nodes/{0}/blockdevices/{1}/partitions/".format(system_id, device_id), None, **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100411 LOG.info(partition)
412 result["partition"] = "Partition created on {0}".format(disk)
413
414 if fs_type != None:
415 data_fs_type = {
416 "fstype": fs_type
417 }
418 partition_id = str(partition["id"])
419 LOG.info("Partition id: " + partition_id)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200420 # TODO validation
421 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
422 system_id, device_id, partition_id), "format", **data_fs_type).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100423 LOG.info(json_res)
424 result["filesystem"] = "Filesystem {0} created".format(fs_type)
425
426 if mount != None:
427 data = {
428 "mount_point": mount
429 }
430
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200431 # TODO validation
432 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
433 system_id, device_id, str(partition['id'])), "mount", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100434 LOG.info(json_res)
435 result["mount"] = "Mount point {0} created".format(mount)
436
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100437 return result
438
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200439
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100440def delete_partition(hostname, disk, partition_name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300441 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100442 Delete partition on device.
443
444 CLI Example:
445
446 .. code-block:: bash
447
448 salt 'maas-node' maasng.delete_partition server_hostname disk_name partition_name
449 salt-call maasng.delete_partition server_hostname disk_name partition_name
450
451 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300452 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100453 result = {}
454 data = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200455 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100456 system_id = get_machine(hostname)["system_id"]
457 LOG.info(system_id)
458
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200459 device_id = _get_blockdevice_id_by_name(hostname, disk)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100460 LOG.info(device_id)
461
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200462 partition_id = _get_partition_id_by_name(hostname, disk, partition_name)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100463
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200464 maas.delete(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
465 system_id, device_id, partition_id)).read()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100466 result["new"] = "Partition {0} deleted".format(partition_name)
467 return result
468
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200469
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100470def delete_partition_by_id(hostname, disk, partition_id):
azvyagintsevbca1f462018-05-25 19:06:46 +0300471 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100472 Delete partition on device. Partition spefified by id of parition
473
474 CLI Example:
475
476 .. code-block:: bash
477
478 salt 'maas-node' maasng.delete_partition_by_id server_hostname disk_name partition_id
479 salt-call maasng.delete_partition_by_id server_hostname disk_name partition_id
480
481 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300482 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100483 result = {}
484 data = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200485 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100486 system_id = get_machine(hostname)["system_id"]
487 LOG.info(system_id)
488
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200489 device_id = _get_blockdevice_id_by_name(hostname, disk)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100490 LOG.info(device_id)
491
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200492 maas.delete(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
493 system_id, device_id, partition_id)).read()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100494 result["new"] = "Partition {0} deleted".format(partition_id)
495 return result
azvyagintsevbca1f462018-05-25 19:06:46 +0300496# END PARTITIONS
497# DISK LAYOUT
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100498
azvyagintsevbca1f462018-05-25 19:06:46 +0300499
500def drop_storage_schema(hostname,disk=None):
501 """
502 #1. Drop lv
503 #2. Drop vg
504 #3. Drop md # need to zero-block?
505 #3. Drop part
506 """
507
508 if __opts__['test']:
509 ret['result'] = None
510 ret['comment'] = 'Storage schema on {0} will be removed'.format(hostname)
511 return ret
512 #TODO validation if exists
513 vgs = list_volume_groups(hostname)
514 for vg in vgs:
515 delete_volume_group(hostname, vg)
516
517 raids = list_raids(hostname)
518 for raid in raids:
519 delete_raid(hostname, raid)
520
521 blocks = list_blockdevices(hostname)
522 for block_d in blocks:
523 partitions = __salt__['maasng.list_partitions'](hostname, block_d)
524 for partition_name, partition in partitions.iteritems():
525 LOG.info('delete partition:\n{}'.format(partition))
526 __salt__['maasng.delete_partition_by_id'](hostname, block_d, partition["id"])
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200527
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100528
529def 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 +0300530 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100531 Update disk layout. Flat or LVM layout supported.
532
533 CLI Example:
534
535 .. code-block:: bash
536
537 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
538 salt-call maasng.update_disk_layout server_hostname lvm root_size=None, root_device=None, volume_group=None, volume_name=None, volume_size=None
539
540 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300541 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100542 result = {}
543 data = {
544 "storage_layout": layout,
545 }
546
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200547 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100548 system_id = get_machine(hostname)["system_id"]
549 LOG.info(system_id)
550
azvyagintsevbca1f462018-05-25 19:06:46 +0300551 if layout == 'custom':
552 drop_storage_schema(hostname)
553 result["new"] = {
554 "storage_layout": layout,
555 }
556
557 return result
558
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100559 if root_size != None:
560 bit_size = str(root_size * 1073741824)
561 LOG.info(bit_size)
562 data["root_size"] = bit_size
563
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100564 if root_device != None:
565 LOG.info(root_device)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200566 data["root_device"] = str(
567 _get_blockdevice_id_by_name(hostname, root_device))
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100568
569 if layout == 'lvm':
570 if volume_group != None:
571 LOG.info(volume_group)
572 data["vg_name"] = volume_group
573 if volume_name != None:
574 LOG.info(volume_name)
575 data["lv_name"] = volume_name
576 if volume_size != None:
577 vol_size = str(volume_size * 1073741824)
578 LOG.info(vol_size)
579 data["lv_size"] = vol_size
580
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200581 # TODO validation
582 json_res = json.loads(maas.post(
583 u"api/2.0/machines/{0}/".format(system_id), "set_storage_layout", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100584 LOG.info(json_res)
585 result["new"] = {
586 "storage_layout": layout,
587 }
588
589 return result
590
azvyagintsevbca1f462018-05-25 19:06:46 +0300591# END DISK LAYOUT
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200592# LVM
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100593
594def list_volume_groups(hostname):
azvyagintsevbca1f462018-05-25 19:06:46 +0300595 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100596 Get list of all volume group on machine.
597
598 CLI Example:
599
600 .. code-block:: bash
601
602 salt 'maas-node' maasng.list_volume_groups server_hostname
603 salt-call maasng.list_volume_groups server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300604 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100605 volume_groups = {}
606
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200607 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100608 system_id = get_machine(hostname)["system_id"]
609 LOG.info(system_id)
610
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200611 # TODO validation if exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100612
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200613 json_res = json.loads(
614 maas.get(u"api/2.0/nodes/{0}/volume-groups/".format(system_id)).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100615 LOG.info(json_res)
616 for item in json_res:
617 volume_groups[item["name"]] = item
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200618 # return
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100619 return volume_groups
620
621
622def get_volume_group(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300623 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100624 Get information about specific volume group on machine.
625
626 CLI Example:
627
628 .. code-block:: bash
629
630 salt 'maas-node' maasng.list_blockdevices server_hostname
631 salt-call maasng.list_blockdevices server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300632 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200633 # TODO validation that exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100634 return list_volume_groups(hostname)[name]
635
636
637def create_volume_group(hostname, volume_group_name, disks=[], partitions=[]):
azvyagintsevbca1f462018-05-25 19:06:46 +0300638 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100639 Create new volume group on machine. Disks or partitions needs to be provided.
640
641 CLI Example:
642
643 .. code-block:: bash
644
645 salt 'maas-node' maasng.create_volume_group volume_group_name, disks=[sda,sdb], partitions=[]
646 salt-call maasng.create_volume_group server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300647 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100648 result = {}
649
650 data = {
651 "name": volume_group_name,
652 }
653
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200654 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100655 system_id = get_machine(hostname)["system_id"]
656 LOG.info(system_id)
657
658 disk_ids = []
659 partition_ids = []
660
661 for disk in disks:
662 p_disk = get_blockdevice(hostname, disk)
663 if p_disk["partition_table_type"] == None:
664 disk_ids.append(str(p_disk["id"]))
665 else:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200666 result["error"] = "Device {0} on machine {1} cointains partition table".format(
667 disk, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100668 return result
669
670 for partition in partitions:
671 try:
672 device = partition.split("-")[0]
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200673 device_part = list_partitions(hostname, device)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100674 partition_ids.append(str(device_part[partition]["id"]))
675 except KeyError:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200676 result["error"] = "Partition {0} does not exists on machine {1}".format(
677 partition, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100678 return result
679
680 data["block_devices"] = disk_ids
681 data["partitions"] = partition_ids
682 LOG.info(partition_ids)
683 LOG.info(partitions)
684
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200685 # TODO validation
686 json_res = json.loads(maas.post(
687 u"api/2.0/nodes/{0}/volume-groups/".format(system_id), None, **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100688 LOG.info(json_res)
689 result["new"] = "Volume group {0} created".format(json_res["name"])
690
691 return result
692
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200693
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100694def delete_volume_group(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300695 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100696 Delete volume group on machine.
697
698 CLI Example:
699
700 .. code-block:: bash
701
702 salt 'maas-node' maasng.delete_volume_group server_hostname vg0
703 salt-call maasng.delete_volume_group server_hostname vg0
azvyagintsevbca1f462018-05-25 19:06:46 +0300704 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100705
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200706 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100707 system_id = get_machine(hostname)["system_id"]
azvyagintsevbca1f462018-05-25 19:06:46 +0300708 LOG.debug('delete_volume_group:{}'.format(system_id))
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100709
azvyagintsevbca1f462018-05-25 19:06:46 +0300710 vg_id = str(_get_volume_group_id_by_name(hostname, name))
711 for vol in get_volumes(hostname, name):
712 delete_volume(hostname,vol,name)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100713
azvyagintsevbca1f462018-05-25 19:06:46 +0300714 #TODO validation
715 json_res = json.loads(maas.delete(u"api/2.0/nodes/{0}/volume-group/{1}/".format(system_id, vg_id)).read() or 'null')
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100716 LOG.info(json_res)
717
718 return True
719
720
721def create_volume(hostname, volume_name, volume_group, size, fs_type=None, mount=None):
azvyagintsevbca1f462018-05-25 19:06:46 +0300722 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100723 Create volume on volume group.
724
725 CLI Example:
726
727 .. code-block:: bash
728
729 salt 'maas-node' maasng.create_volume server_hostname volume_name, volume_group, size, fs_type=None, mount=None
730 salt-call maasng.create_volume server_hostname volume_name, volume_group, size, fs_type=None, mount=None
azvyagintsevbca1f462018-05-25 19:06:46 +0300731 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100732
733 data = {
734 "name": volume_name,
735 }
736
737 value, unit = size[:-1], size[-1]
738 bit_size = str(int(value) * SIZE[unit])
739 LOG.info(bit_size)
740
741 data["size"] = bit_size
742
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200743 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100744 system_id = get_machine(hostname)["system_id"]
745 LOG.info(system_id)
746
747 volume_group_id = str(_get_volume_group_id_by_name(hostname, volume_group))
748
749 LOG.info(volume_group_id)
750
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200751 # TODO validation
752 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/volume-group/{1}/".format(
753 system_id, volume_group_id), "create_logical_volume", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100754 LOG.info(json_res)
755
756 if fs_type != None or mount != None:
azvyagintsevbca1f462018-05-25 19:06:46 +0300757 ret = create_volume_filesystem(hostname, volume_group + "-" + volume_name, fs_type, mount)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100758
759 return True
760
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100761
azvyagintsevbca1f462018-05-25 19:06:46 +0300762def delete_volume(hostname, volume_name, volume_group):
763 """
764 Delete volume from volume group.
765 Tips: maas always use 'volume_group-volume_name' name schema.Example: 'vg0-glusterfs'
766 This function expexts same format.
767
768 CLI Example:
769
770 .. code-block:: bash
771
772 salt 'maas-node' maasng.delete_volume server_hostname volume_name volume_group
773 salt 'maas-node' maasng.delete_volume server_hostname vg0-vol0 vg0
774 salt-call maasng.delete_volume server_hostname volume_name volume_group
775 """
776
777 maas=_create_maas_client()
778 system_id = get_machine(hostname)["system_id"]
779 LOG.debug('delete_volume:{}'.format(system_id))
780
781 volume_group_id = str(_get_volume_group_id_by_name(hostname, volume_group))
782 volume_id = str(_get_volume_id_by_name(hostname, volume_name, volume_group))
783
784 if None in [volume_group_id, volume_id]:
785 return False
786
787 data = {
788 "id": volume_id,
789 }
790
791 #TODO validation
792 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/volume-group/{1}/".format(system_id, volume_group_id), "delete_logical_volume", **data).read() or 'null')
793 return True
794
795
796def get_volumes(hostname, vg_name):
797 """
798 Get list of volumes in volume group.
799 """
800 volumes = {}
801 _volumes = list_volume_groups(hostname)[vg_name].get('logical_volumes', False)
802 if _volumes:
803 for item in _volumes:
804 volumes[item["name"]] = item
805 return volumes
806
807# END LVM
808
809
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200810def create_volume_filesystem(hostname, device, fs_type=None, mount=None):
811
812 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100813 system_id = get_machine(hostname)["system_id"]
814
815 blockdevices_id = _get_blockdevice_id_by_name(hostname, device)
816 data = {}
817 if fs_type != None:
818 data["fstype"] = fs_type
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200819 # TODO validation
820 json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
821 system_id, blockdevices_id), "format", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100822 LOG.info(json_res)
823
824 if mount != None:
825 data["mount_point"] = mount
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200826 # TODO validation
827 json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
828 system_id, blockdevices_id), "mount", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100829 LOG.info(json_res)
830
831 return True
832
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100833
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100834def set_boot_disk(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300835 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100836 Create volume on volume group.
837
838 CLI Example:
839
840 .. code-block:: bash
841
842 salt 'maas-node' maasng.set_boot_disk server_hostname disk_name
843 salt-call maasng.set_boot_disk server_hostname disk_name
azvyagintsevbca1f462018-05-25 19:06:46 +0300844 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100845 data = {}
846 result = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200847 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100848 system_id = get_machine(hostname)["system_id"]
849 blockdevices_id = _get_blockdevice_id_by_name(hostname, name)
850
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200851 maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
852 system_id, blockdevices_id), "set_boot_disk", **data).read()
853 # TODO validation for error response (disk does not exists and node does not exists)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100854 result["new"] = "Disk {0} was set as bootable".format(name)
855
856 return result
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200857
azvyagintsevbca1f462018-05-25 19:06:46 +0300858# NETWORKING
859
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200860
861def list_fabric():
azvyagintsevbca1f462018-05-25 19:06:46 +0300862 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200863 Get list of all fabric
864
865 CLI Example:
866
867 .. code-block:: bash
868
869 salt 'maas-node' maasng.list_fabric
azvyagintsevbca1f462018-05-25 19:06:46 +0300870 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200871 fabrics = {}
872 maas = _create_maas_client()
873 json_res = json.loads(maas.get(u'api/2.0/fabrics/').read())
874 LOG.info(json_res)
875 for item in json_res:
876 fabrics[item["name"]] = item
877 return fabrics
878
879
880def create_fabric(name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300881 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200882 Create new fabric.
883
884 CLI Example:
885
886 .. code-block:: bash
887
888 salt 'maas-node' maasng.create_fabric
azvyagintsevbca1f462018-05-25 19:06:46 +0300889 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200890 result = {}
891 data = {
892 "name": name,
893 "description": '',
894 "class_type": '',
895
896 }
897
898 maas = _create_maas_client()
899 json_res = json.loads(maas.post(u"api/2.0/fabrics/", None, **data).read())
900 LOG.info(json_res)
901 result["new"] = "Fabrics {0} created".format(json_res["name"])
902 return result
903
904
905def list_subnet():
azvyagintsevbca1f462018-05-25 19:06:46 +0300906 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200907 Get list of all subnets
908
909 CLI Example:
910
911 .. code-block:: bash
912
913 salt 'maas-node' maasng.list_subnet
azvyagintsevbca1f462018-05-25 19:06:46 +0300914 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200915 subnets = {}
916 maas = _create_maas_client()
917 json_res = json.loads(maas.get(u'api/2.0/subnets/').read())
918 LOG.info(json_res)
919 for item in json_res:
920 subnets[item["name"]] = item
921 return subnets
922
923
924def list_vlans(fabric):
azvyagintsevbca1f462018-05-25 19:06:46 +0300925 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200926 Get list of all vlans for specific fabric
927
928 CLI Example:
929
930 .. code-block:: bash
931
932 salt 'maas-node' maasng.list_vlans
azvyagintsevbca1f462018-05-25 19:06:46 +0300933 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200934 vlans = {}
935 maas = _create_maas_client()
936 fabric_id = get_fabric(fabric)
937
938 json_res = json.loads(
939 maas.get(u'api/2.0/fabrics/{0}/vlans/'.format(fabric_id)).read())
940 LOG.info(json_res)
941 for item in json_res:
942 vlans[item["name"]] = item
943 return vlans
944
945
946def get_fabric(fabric):
azvyagintsevbca1f462018-05-25 19:06:46 +0300947 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200948 Get id for specific fabric
949
950 CLI Example:
951
952 .. code-block:: bash
953
954 salt-call maasng.get_fabric fabric_name
azvyagintsevbca1f462018-05-25 19:06:46 +0300955 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200956 try:
957 return list_fabric()[fabric]['id']
958 except KeyError:
959 return {"error": "Frabic not found on MaaS server"}
960
961
Pavel Cizinsky864a3292018-05-25 16:24:48 +0200962def update_vlan(name, fabric, vid, description, primary_rack, dhcp_on=False):
azvyagintsevbca1f462018-05-25 19:06:46 +0300963 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200964 Update vlan
965
966 CLI Example:
967
968 .. code-block:: bash
969
970 salt 'maas-node' maasng.update_vlan name, fabric, vid, description, dhcp_on
azvyagintsevbca1f462018-05-25 19:06:46 +0300971 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200972 result = {}
973
974 data = {
975 "name": name,
976 "dhcp_on": str(dhcp_on),
977 "description": description,
Pavel Cizinsky864a3292018-05-25 16:24:48 +0200978 "primary_rack": primary_rack,
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200979 }
980 maas = _create_maas_client()
981 fabric_id = get_fabric(fabric)
982
983 json_res = json.loads(maas.put(
984 u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id, vid), **data).read())
azvyagintsev3ff2ef12018-06-01 21:30:45 +0300985 LOG.debug("update_vlan:{}".format(json_res))
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200986 result["new"] = "Vlan {0} was updated".format(json_res["name"])
987
988 return result
azvyagintsevbca1f462018-05-25 19:06:46 +0300989
990# END NETWORKING
azvyagintsev3ff2ef12018-06-01 21:30:45 +0300991
992# MAAS CONFIG SECTION
993
994
995def _get_boot_source_id_by_url(url):
996 # FIXME: fix ret\validation
997 try:
998 bs_id = get_boot_source(url=url)["id"]
999 except KeyError:
1000 return {"error": "boot-source:{0} not exist!".format(url)}
1001 return bs_id
1002
1003
1004def get_boot_source(url=None):
1005 """
1006 Read a boot source by url. If url not specified - return all.
1007
1008 CLI Example:
1009
1010 .. code-block:: bash
1011
1012 salt 'maas-node' maasng.get_boot_source url
1013
1014 """
1015 boot_sources = {}
1016 maas = _create_maas_client()
1017 json_res = json.loads(maas.get(u'api/2.0/boot-sources/').read() or 'null')
1018 for item in json_res:
1019 boot_sources[str(item["url"])] = item
1020 if url:
1021 return boot_sources.get(url, {})
1022 return boot_sources
1023
1024
1025def delete_boot_source(url, bs_id=None):
1026 """
1027 Delete a boot source by url.
1028
1029 CLI Example:
1030
1031 .. code-block:: bash
1032
1033 sal 'maas-node' maasng.delete url
1034
1035 """
1036 result = {}
1037 if not bs_id:
1038 bs_id = _get_boot_source_id_by_url(url)
1039 maas = _create_maas_client()
1040 json_res = json.loads(maas.delete(
1041 u'/api/2.0/boot-sources/{0}/'.format(bs_id)).read() or 'null')
1042 LOG.debug("delete_boot_source:{}".format(json_res))
1043 result["new"] = "Boot-resource {0} deleted".format(url)
1044 return result
1045
1046
1047def boot_sources_delete_all_others(except_urls=[]):
1048 """
1049 Delete all boot-sources, except defined in 'except_urls' list.
1050 """
1051 result = {}
1052 maas_boot_sources = get_boot_source()
1053 if 0 in [len(except_urls), len(maas_boot_sources)]:
1054 result['result'] = None
1055 result[
1056 "comment"] = "Exclude or maas sources for delete empty. No changes goinng to be."
1057 return result
1058 for url in maas_boot_sources.keys():
1059 if url not in except_urls:
1060 LOG.info("Removing boot-source:{}".format(url))
1061 boot_resources_import(action='stop_import', wait=True)
1062 result["changes"] = delete_boot_source(url)
1063 return result
1064
1065
1066def create_boot_source(url, keyring_filename='', keyring_data='', wait=False):
1067 """
1068 Create and import maas boot-source: link to maas-ephemeral repo
1069 Be aware, those step will import resource to rack ctrl, but you also need to import
1070 them into the region!
1071
1072
1073 :param url: The URL of the BootSource.
1074 :param keyring_filename: The path to the keyring file for this BootSource.
1075 :param keyring_data: The GPG keyring for this BootSource, base64-encoded data.
1076
1077 """
1078
1079 # TODO: not work with 'update' currently => keyring update may fail.
1080 result = {}
1081
1082 data = {
1083 "url": url,
1084 "keyring_filename": keyring_filename,
1085 "keyring_data": str(keyring_data),
1086 }
1087
1088 maas = _create_maas_client()
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001089 if url in get_boot_source():
1090 result['result'] = None
1091 result["comment"] = "boot resource already exist"
1092 return result
1093
1094 # NOTE: maas.post will return 400, if url already defined.
1095 json_res = json.loads(
1096 maas.post(u'api/2.0/boot-sources/', None, **data).read())
1097 if wait:
1098 LOG.debug(
1099 "Sleep for 5s,to get MaaS some time to process previous request")
1100 time.sleep(5)
1101 ret = boot_resources_is_importing(wait=True)
1102 if ret is dict:
1103 return ret
1104 LOG.debug("create_boot_source:{}".format(json_res))
1105 result["new"] = "boot resource {0} was created".format(json_res["url"])
1106
1107 return result
1108
1109
1110def boot_resources_import(action='import', wait=False):
1111 """
1112 import/stop_import the boot resources.
1113
1114 :param action: import\stop_import
1115 :param wait: True\False. Wait till process finished.
1116
1117 CLI Example:
1118
1119 .. code-block:: bash
1120
1121 salt 'maas-node' maasng.boot_resources_import action='import'
1122
1123 """
1124 maas = _create_maas_client()
1125 # Have no idea why, but usual jsonloads not work here..
1126 imp = maas.post(u'api/2.0/boot-resources/', action)
1127 if imp.code == 200:
1128 LOG.debug('boot_resources_import:{}'.format(imp.readline()))
1129 if wait:
1130 boot_resources_is_importing(wait=True)
1131 return True
1132 else:
1133 return False
1134
1135
1136def boot_resources_is_importing(wait=False):
1137 maas = _create_maas_client()
1138 result = {}
1139 if wait:
1140 started_at = time.time()
1141 poll_time = 5
1142 timeout = 60 * 15
1143 while boot_resources_is_importing(wait=False):
1144 c_timeout = timeout - (time.time() - started_at)
1145 if c_timeout <= 0:
1146 result['result'] = False
1147 result["comment"] = "Boot-resources import not finished in time"
1148 return result
1149 LOG.info(
1150 "Waiting boot-resources import done\n"
1151 "sleep for:{}s "
1152 "Left:{}/{}s".format(poll_time, round(c_timeout), timeout))
1153 time.sleep(poll_time)
1154 return json.loads(
1155 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1156 else:
1157 return json.loads(
1158 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1159
1160#####
1161#def boot_sources_selections_delete_all_others(except_urls=[]):
1162# """
1163# """
1164# result = {}
1165# return result
1166
1167
1168def is_boot_source_selections_in(dict1, list1):
1169 """
1170 Check that requested boot-selection already in maas bs selections, if True- return bss id.
1171 # FIXME: those hack check doesn't look good.
1172 """
1173 for bs in list1:
1174 same = set(dict1.keys()) & set(bs.keys())
1175 if all(elem in same for elem in
1176 ['os', 'release', 'arches', 'subarches', 'labels']):
1177 LOG.debug(
1178 "boot-selection in maas:{0}\nlooks same to requested:{1}".format(
1179 bs, dict1))
1180 return bs['id']
1181 return False
1182
1183
1184def get_boot_source_selections(bs_url):
1185 """
1186 Get boot-source selections.
1187 """
1188 # check for key_error!
1189 bs_id = _get_boot_source_id_by_url(bs_url)
1190 maas = _create_maas_client()
1191 json_res = json.loads(
1192 maas.get(u'/api/2.0/boot-sources/{0}/selections/'.format(bs_id)).read())
1193 LOG.debug(
1194 "get_boot_source_selections for url:{} \n{}".format(bs_url, json_res))
1195 return json_res
1196
1197
1198def create_boot_source_selections(bs_url, os, release, arches="*",
1199 subarches="*", labels="*", wait=True):
1200 """
1201 Create a new boot source selection for bs_url.
1202 :param os: The OS (e.g. ubuntu, centos) for which to import resources.Required.
1203 :param release: The release for which to import resources. Required.
1204 :param arches: The architecture list for which to import resources.
1205 :param subarches: The subarchitecture list for which to import resources.
1206 :param labels: The label lists for which to import resources.
1207 """
1208
azvyagintsevcb54d142018-06-19 16:18:32 +03001209 result = { "result" : True, 'name' : bs_url, 'changes' : None }
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001210
1211 data = {
1212 "os": os,
1213 "release": release,
1214 "arches": arches,
1215 "subarches": subarches,
1216 "labels": labels,
1217 }
1218
1219 maas = _create_maas_client()
1220 bs_id = _get_boot_source_id_by_url(bs_url)
1221 # TODO add pre-create verify
1222 maas_bs_s = get_boot_source_selections(bs_url)
1223 if is_boot_source_selections_in(data, maas_bs_s):
1224 result["result"] = True
1225 result[
1226 "comment"] = 'Requested boot-source selection for {0} already exist.'.format(
1227 bs_url)
1228 return result
1229
1230 # NOTE: maas.post will return 400, if url already defined.
azvyagintsevcb54d142018-06-19 16:18:32 +03001231 # Also, maas need's some time to import info about stream.
1232 # unfortunatly, maas don't have any call to check stream-import-info - so, we need to implement
1233 # at least simple retry ;(
1234 json_res = False
1235 poll_time = 5
1236 for i in range(0,5):
1237 try:
1238 json_res = json.loads(
1239 maas.post(u'api/2.0/boot-sources/{0}/selections/'.format(bs_id), None,
1240 **data).read())
1241 except Exception as inst:
1242 m = inst.readlines()
1243 LOG.warning("boot_source_selections catch error during processing. Most-probably, streams not imported yet.Sleep:{}s\nRetry:{}/5".format(poll_time,i))
1244 LOG.warning("Message:{0}".format(m))
1245 time.sleep(poll_time)
1246 continue
1247 break
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001248 LOG.debug("create_boot_source_selections:{}".format(json_res))
azvyagintsevcb54d142018-06-19 16:18:32 +03001249 if not json_res:
1250 result["result"] = False
1251 result[
1252 "comment"] = 'Failed to create requested boot-source selection for {0}.'.format(bs_url)
1253 return result
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001254 if wait:
1255 LOG.debug(
1256 "Sleep for 5s,to get MaaS some time to process previous request")
1257 time.sleep(5)
1258 ret = boot_resources_import(action='import', wait=True)
1259 if ret is dict:
1260 return ret
azvyagintsevcb54d142018-06-19 16:18:32 +03001261 result["comment"] = "boot-source selection for {0} was created".format(bs_url)
1262 result["new"] = data
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001263
1264 return result
1265
1266# END MAAS CONFIG SECTION
azvyagintsevcb54d142018-06-19 16:18:32 +03001267
1268# RACK CONTROLLERS SECTION
1269
1270
1271def get_rack(hostname):
1272 """
1273 Get information about specified rackd
1274
1275 CLI Example:
1276
1277 .. code-block:: bash
1278
1279 salt-call maasng.get_rack rack_hostname
1280 """
1281 try:
1282 return list_racks()[hostname]
1283 except KeyError:
1284 return {"error": "rack:{} not found on MaaS server".format(hostname)}
1285
1286
1287def list_racks():
1288 """
1289 Get list of all rack controllers from maas server
1290
1291 CLI Example:
1292
1293 .. code-block:: bash
1294
1295 salt-call maasng.list_racks
1296 """
1297 racks = {}
1298 maas = _create_maas_client()
1299 json_res = json.loads(
1300 maas.get(u"/api/2.0/rackcontrollers/").read() or 'null')
1301 for item in json_res:
1302 racks[item["hostname"]] = item
1303 return racks
1304
1305
1306def sync_bs_to_rack(hostname=None):
1307 """
1308 Sync RACK boot-sources with REGION. If no hostname probided - sync to all.
1309
1310 CLI Example:
1311
1312 .. code-block:: bash
1313
1314 salt-call maasng.sync_bs_to_rack rack_hostname
1315 """
1316 ret = {}
1317 maas = _create_maas_client()
1318 if not hostname:
1319 LOG.info("boot-sources sync initiated for ALL Rack's")
1320 # Convert to json-like format
1321 json_res = json.loads('["{0}"]'.format(
1322 maas.post(u"/api/2.0/rackcontrollers/",
1323 'import_boot_images').read()))
1324 LOG.debug("sync_bs_to_rack:{}".format(json_res))
1325 ret['result'] = True
1326 ret['comment'] = "boot-sources sync initiated for ALL Rack's"
1327 return ret
1328 LOG.info("boot-sources sync initiated for RACK:{0}".format(hostname))
1329 # Convert to json-like format
1330 json_res = json.loads('["{0}"]'.format(maas.post(
1331 u"/api/2.0/rackcontrollers/{0}/".format(
1332 get_rack(hostname)['system_id']),
1333 'import_boot_images').read()))
1334 LOG.debug("sync_bs_to_rack:{}".format(json_res))
1335 ret['result'] = True
1336 ret['comment'] = "boot-sources sync initiated for {0} Rack's".format(
1337 hostname)
1338 return
1339
1340
1341def rack_list_boot_imgs(hostname):
1342 ret = {}
1343 maas = _create_maas_client()
1344 LOG.debug("rack_list_boot_imgs:{}".format(hostname))
1345 ret = json.loads(maas.get(u"/api/2.0/rackcontrollers/{0}/".format(
1346 get_rack(hostname)['system_id']), 'list_boot_images').read() or 'null')
1347 return ret
1348
1349
1350def is_rack_synced(hostname):
1351 rez = rack_list_boot_imgs(hostname)['status']
1352 if rez == 'synced':
1353 return True
1354 return False
1355
1356# TODO do we actually need _exact_ check per-pack?
1357# def wait_for_images_on_rack(hostname):
1358#
1359# """
1360# WA function, to be able check that RACK actually done SYNC images
1361# for REQUIRED images at least.
1362# Required image to be fetched from
1363# reclass:maas:region:boot_sources_selections:[keys]:os/release formation
1364#
1365# CLI Example:
1366#
1367# .. code-block:: bash
1368#
1369# salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
1370# """
1371# try:
1372# bss = __salt__['config.get']('maas')['region']['boot_sources_selections']
1373# except KeyError:
1374# ret['result'] = None
1375# ret['comment'] = "boot_sources_selections definition for sync not found."
1376# return ret
1377# s_names = []
1378# # Format u'name': u'ubuntu/xenial'
1379# for v in bss.values():s_names.append("{0}/{1}".format(v['os'],v['release']))
1380# # Each names, should be in rack and whole rack should be in sync-ed state
1381
1382
1383def sync_and_wait_bs_to_all_racks():
1384 """
1385 Sync ALL rack's with regions source images.
1386
1387 CLI Example:
1388
1389 .. code-block:: bash
1390
1391 salt-call maasng.sync_and_wait_bs_to_all_racks
1392 """
1393 sync_bs_to_rack()
1394 for rack in list_racks().keys():
1395 wait_for_sync_bs_to_rack(hostname=rack)
1396 return True
1397
1398
1399def wait_for_sync_bs_to_rack(hostname=None):
1400 """
1401 Wait for boot images sync finished, on exact rack
1402
1403 CLI Example:
1404
1405 .. code-block:: bash
1406
1407 salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
1408 """
1409 ret = {}
1410 started_at = time.time()
1411 poll_time = 5
1412 timeout = 60 * 15
1413 while not is_rack_synced(hostname):
1414 c_timeout = timeout - (time.time() - started_at)
1415 if c_timeout <= 0:
1416 ret['result'] = False
1417 ret[
1418 "comment"] = "Boot-resources sync on rackd:{0}" \
1419 "not finished in time".format(
1420 hostname)
1421 return ret
1422 LOG.info(
1423 "Waiting boot-resources sync done to rack:{0}\n"
1424 "sleep for:{1}s "
1425 "Left:{2}/{3}s".format(hostname, poll_time, round(c_timeout),
1426 timeout))
1427 time.sleep(poll_time)
1428 ret['result'] = is_rack_synced(hostname)
1429 ret["comment"] = "Boot-resources sync on rackd:{0} finished".format(
1430 hostname)
1431 return ret
1432
1433# END RACK CONTROLLERS SECTION