blob: f14b18e3aaa50ba23f154c35da88bb5c095f8280 [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
43HAS_MASS = False
44try:
45 from maas_client import MAASClient, MAASDispatcher, MAASOAuth
46 HAS_MASS = True
47except ImportError:
48 LOG.debug('Missing MaaS client module is Missing. Skipping')
49
50
51def __virtual__():
azvyagintsevbca1f462018-05-25 19:06:46 +030052 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +010053 Only load this module if maas-client
54 is installed on this minion.
azvyagintsevbca1f462018-05-25 19:06:46 +030055 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +010056 if HAS_MASS:
57 return 'maasng'
58 return False
59
60
61APIKEY_FILE = '/var/lib/maas/.maas_credentials'
62
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020063
Ondrej Smolab57a23b2018-01-24 11:18:24 +010064def _format_data(data):
65 class Lazy:
66 def __str__(self):
67 return ' '.join(['{0}={1}'.format(k, v)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020068 for k, v in data.iteritems()])
Ondrej Smolab57a23b2018-01-24 11:18:24 +010069 return Lazy()
70
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020071
azvyagintsev58947072018-06-29 12:09:48 +030072def _create_maas_client(api_url=None):
73 if not api_url:
74 api_url = 'http://localhost:5240/MAAS'
Ondrej Smolab57a23b2018-01-24 11:18:24 +010075 global APIKEY_FILE
76 try:
77 api_token = file(APIKEY_FILE).read().splitlines()[-1].strip()\
78 .split(':')
79 except:
80 LOG.exception('token')
81 auth = MAASOAuth(*api_token)
Ondrej Smolab57a23b2018-01-24 11:18:24 +010082 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
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200102 volume_name = str("%s-%s" % (volume_group, volume_name))
103 # TODO validation
azvyagintsevbca1f462018-05-25 19:06:46 +0300104 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"]
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200236 # TODO validation
237 json_res = json.loads(
238 maas.get(u"api/2.0/nodes/{0}/raids/".format(system_id)).read())
azvyagintsevbca1f462018-05-25 19:06:46 +0300239 LOG.debug('list_raids:{} {}'.format(system_id, json_res))
240 for item in json_res:
241 raids[item["name"]] = item
242 return raids
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100243
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200244
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100245def get_raid(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300246 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100247 Get information about specific raid on machine
248
249 CLI Example:
250
251 .. code-block:: bash
252
253 salt-call maasng.get_raids server_hostname md0
azvyagintsevbca1f462018-05-25 19:06:46 +0300254 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100255
256 return list_raids(hostname)[name]
257
258
azvyagintsevbca1f462018-05-25 19:06:46 +0300259def _get_raid_id_by_name(hostname, raid_name):
260 return get_raid(hostname, raid_name)['id']
261
262
263def delete_raid(hostname, raid_name):
264 """
265 Delete RAID on a machine.
266
267 CLI Example:
268
269 .. code-block:: bash
270
271 salt 'maas-node' maasng.delete_raid server_hostname raid_name
272 salt-call maasng.delete_raid server_hostname raid_name
273 """
274 result = {}
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200275 maas = _create_maas_client()
azvyagintsevbca1f462018-05-25 19:06:46 +0300276 system_id = get_machine(hostname)["system_id"]
277 raid_id = _get_raid_id_by_name(hostname, raid_name)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200278 LOG.debug('delete_raid: {} {}'.format(system_id, raid_id))
279 maas.delete(
280 u"api/2.0/nodes/{0}/raid/{1}/".format(system_id, raid_id)).read()
azvyagintsevbca1f462018-05-25 19:06:46 +0300281
282 result["new"] = "Raid {0} deleted".format(raid_name)
283 return result
284
285# END RAID SECTION
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200286# BLOCKDEVICES SECTION
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100287
azvyagintsevbca1f462018-05-25 19:06:46 +0300288
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100289def list_blockdevices(hostname):
azvyagintsevbca1f462018-05-25 19:06:46 +0300290 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100291 Get list of all blockdevices (disks) on machine
292
293 CLI Example:
294
295 .. code-block:: bash
296
297 salt 'maas-node' maasng.list_blockdevices server_hostname
298 salt-call maasng.list_blockdevices server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300299 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100300 ret = {}
301
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200302 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100303 system_id = get_machine(hostname)["system_id"]
304 LOG.info(system_id)
305
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200306 # TODO validation if exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100307
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200308 json_res = json.loads(
309 maas.get(u"api/2.0/nodes/{0}/blockdevices/".format(system_id)).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100310 LOG.info(json_res)
311 for item in json_res:
312 ret[item["name"]] = item
313
314 return ret
315
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200316
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100317def get_blockdevice(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300318 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100319 Get information about blockdevice (disk) on machine
320
321 CLI Example:
322
323 .. code-block:: bash
324
325 salt 'maas-node' maasng.get_blockdevice server_hostname sda
326 salt-call maasng.get_blockdevice server_hostname sda
azvyagintsevbca1f462018-05-25 19:06:46 +0300327 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100328
329 return list_blockdevices(hostname)[name]
330
azvyagintsevbca1f462018-05-25 19:06:46 +0300331# END BLOCKDEVICES SECTION
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200332# PARTITIONS
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100333
azvyagintsevbca1f462018-05-25 19:06:46 +0300334
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100335def list_partitions(hostname, device):
azvyagintsevbca1f462018-05-25 19:06:46 +0300336 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100337 Get list of all partitions on specific device located on specific machine
338
339 CLI Example:
340
341 .. code-block:: bash
342
343 salt 'maas-node' maasng.list_partitions server_hostname sda
344 salt-call maasng.list_partitions server_hostname sda
azvyagintsevbca1f462018-05-25 19:06:46 +0300345 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100346 ret = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200347 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100348 system_id = get_machine(hostname)["system_id"]
349 LOG.info(system_id)
350
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200351 partitions = get_blockdevice(hostname, device)["partitions"]
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100352 LOG.info(partitions)
353
354 #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 +0200355 # LOG.info(json_res)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100356
357 if len(device) > 0:
358 for item in partitions:
359 name = item["path"].split('/')[-1]
360 ret[name] = item
361
362 return ret
363
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200364
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100365def get_partition(hostname, device, partition):
azvyagintsevbca1f462018-05-25 19:06:46 +0300366 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100367 Get information about specific parition on device located on machine
368
369 CLI Example:
370
371 .. code-block:: bash
372
373 salt 'maas-node' maasng.get_partition server_hostname disk_name partition
374 salt-call maasng.get_partition server_hostname disk_name partition
375
376 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300377 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100378
379 return list_partitions(partition)[name]
380
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200381
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100382def create_partition(hostname, disk, size, fs_type=None, mount=None):
azvyagintsevbca1f462018-05-25 19:06:46 +0300383 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100384 Create new partition on device.
385
386 CLI Example:
387
388 .. code-block:: bash
389
390 salt 'maas-node' maasng.create_partition server_hostname disk_name 10 ext4 "/"
391 salt-call maasng.create_partition server_hostname disk_name 10 ext4 "/"
azvyagintsevbca1f462018-05-25 19:06:46 +0300392 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200393 # TODO validation
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100394 result = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200395 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100396 system_id = get_machine(hostname)["system_id"]
397 LOG.info(system_id)
398
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200399 device_id = _get_blockdevice_id_by_name(hostname, disk)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100400 LOG.info(device_id)
401
402 value, unit = size[:-1], size[-1]
403 calc_size = str(int(value) * SIZE[unit])
404 LOG.info(calc_size)
405
406 data = {
407 "size": calc_size
408 }
409
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200410 # TODO validation
411 partition = json.loads(maas.post(
412 u"api/2.0/nodes/{0}/blockdevices/{1}/partitions/".format(system_id, device_id), None, **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100413 LOG.info(partition)
414 result["partition"] = "Partition created on {0}".format(disk)
415
416 if fs_type != None:
417 data_fs_type = {
418 "fstype": fs_type
419 }
420 partition_id = str(partition["id"])
421 LOG.info("Partition id: " + partition_id)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200422 # TODO validation
423 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
424 system_id, device_id, partition_id), "format", **data_fs_type).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100425 LOG.info(json_res)
426 result["filesystem"] = "Filesystem {0} created".format(fs_type)
427
428 if mount != None:
429 data = {
430 "mount_point": mount
431 }
432
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200433 # TODO validation
434 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
435 system_id, device_id, str(partition['id'])), "mount", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100436 LOG.info(json_res)
437 result["mount"] = "Mount point {0} created".format(mount)
438
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100439 return result
440
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200441
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100442def delete_partition(hostname, disk, partition_name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300443 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100444 Delete partition on device.
445
446 CLI Example:
447
448 .. code-block:: bash
449
450 salt 'maas-node' maasng.delete_partition server_hostname disk_name partition_name
451 salt-call maasng.delete_partition server_hostname disk_name partition_name
452
453 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300454 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100455 result = {}
456 data = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200457 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100458 system_id = get_machine(hostname)["system_id"]
459 LOG.info(system_id)
460
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200461 device_id = _get_blockdevice_id_by_name(hostname, disk)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100462 LOG.info(device_id)
463
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200464 partition_id = _get_partition_id_by_name(hostname, disk, partition_name)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100465
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200466 maas.delete(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
467 system_id, device_id, partition_id)).read()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100468 result["new"] = "Partition {0} deleted".format(partition_name)
469 return result
470
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200471
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100472def delete_partition_by_id(hostname, disk, partition_id):
azvyagintsevbca1f462018-05-25 19:06:46 +0300473 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100474 Delete partition on device. Partition spefified by id of parition
475
476 CLI Example:
477
478 .. code-block:: bash
479
480 salt 'maas-node' maasng.delete_partition_by_id server_hostname disk_name partition_id
481 salt-call maasng.delete_partition_by_id server_hostname disk_name partition_id
482
483 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300484 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100485 result = {}
486 data = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200487 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100488 system_id = get_machine(hostname)["system_id"]
489 LOG.info(system_id)
490
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200491 device_id = _get_blockdevice_id_by_name(hostname, disk)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100492 LOG.info(device_id)
493
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200494 maas.delete(u"api/2.0/nodes/{0}/blockdevices/{1}/partition/{2}".format(
495 system_id, device_id, partition_id)).read()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100496 result["new"] = "Partition {0} deleted".format(partition_id)
497 return result
azvyagintsevbca1f462018-05-25 19:06:46 +0300498# END PARTITIONS
499# DISK LAYOUT
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100500
azvyagintsevbca1f462018-05-25 19:06:46 +0300501
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200502def drop_storage_schema(hostname, disk=None):
azvyagintsevbca1f462018-05-25 19:06:46 +0300503 """
504 #1. Drop lv
505 #2. Drop vg
506 #3. Drop md # need to zero-block?
507 #3. Drop part
508 """
509
510 if __opts__['test']:
511 ret['result'] = None
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200512 ret['comment'] = 'Storage schema on {0} will be removed'.format(
513 hostname)
azvyagintsevbca1f462018-05-25 19:06:46 +0300514 return ret
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200515 # TODO validation if exists
azvyagintsevbca1f462018-05-25 19:06:46 +0300516 vgs = list_volume_groups(hostname)
517 for vg in vgs:
518 delete_volume_group(hostname, vg)
519
520 raids = list_raids(hostname)
521 for raid in raids:
522 delete_raid(hostname, raid)
523
524 blocks = list_blockdevices(hostname)
525 for block_d in blocks:
526 partitions = __salt__['maasng.list_partitions'](hostname, block_d)
527 for partition_name, partition in partitions.iteritems():
528 LOG.info('delete partition:\n{}'.format(partition))
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200529 __salt__['maasng.delete_partition_by_id'](
530 hostname, block_d, partition["id"])
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200531
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100532
533def 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 +0300534 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100535 Update disk layout. Flat or LVM layout supported.
536
537 CLI Example:
538
539 .. code-block:: bash
540
541 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
542 salt-call maasng.update_disk_layout server_hostname lvm root_size=None, root_device=None, volume_group=None, volume_name=None, volume_size=None
543
544 root_size = size in GB
azvyagintsevbca1f462018-05-25 19:06:46 +0300545 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100546 result = {}
547 data = {
548 "storage_layout": layout,
549 }
550
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200551 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100552 system_id = get_machine(hostname)["system_id"]
553 LOG.info(system_id)
554
azvyagintsevbca1f462018-05-25 19:06:46 +0300555 if layout == 'custom':
556 drop_storage_schema(hostname)
557 result["new"] = {
558 "storage_layout": layout,
559 }
560
561 return result
562
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100563 if root_size != None:
564 bit_size = str(root_size * 1073741824)
565 LOG.info(bit_size)
566 data["root_size"] = bit_size
567
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100568 if root_device != None:
569 LOG.info(root_device)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200570 data["root_device"] = str(
571 _get_blockdevice_id_by_name(hostname, root_device))
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100572
573 if layout == 'lvm':
574 if volume_group != None:
575 LOG.info(volume_group)
576 data["vg_name"] = volume_group
577 if volume_name != None:
578 LOG.info(volume_name)
579 data["lv_name"] = volume_name
580 if volume_size != None:
581 vol_size = str(volume_size * 1073741824)
582 LOG.info(vol_size)
583 data["lv_size"] = vol_size
584
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200585 # TODO validation
586 json_res = json.loads(maas.post(
587 u"api/2.0/machines/{0}/".format(system_id), "set_storage_layout", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100588 LOG.info(json_res)
589 result["new"] = {
590 "storage_layout": layout,
591 }
592
593 return result
594
azvyagintsevbca1f462018-05-25 19:06:46 +0300595# END DISK LAYOUT
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200596# LVM
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100597
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200598
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100599def list_volume_groups(hostname):
azvyagintsevbca1f462018-05-25 19:06:46 +0300600 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100601 Get list of all volume group on machine.
602
603 CLI Example:
604
605 .. code-block:: bash
606
607 salt 'maas-node' maasng.list_volume_groups server_hostname
608 salt-call maasng.list_volume_groups server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300609 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100610 volume_groups = {}
611
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200612 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100613 system_id = get_machine(hostname)["system_id"]
614 LOG.info(system_id)
615
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200616 # TODO validation if exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100617
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200618 json_res = json.loads(
619 maas.get(u"api/2.0/nodes/{0}/volume-groups/".format(system_id)).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100620 LOG.info(json_res)
621 for item in json_res:
622 volume_groups[item["name"]] = item
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200623 # return
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100624 return volume_groups
625
626
627def get_volume_group(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300628 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100629 Get information about specific volume group on machine.
630
631 CLI Example:
632
633 .. code-block:: bash
634
635 salt 'maas-node' maasng.list_blockdevices server_hostname
636 salt-call maasng.list_blockdevices server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300637 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200638 # TODO validation that exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100639 return list_volume_groups(hostname)[name]
640
641
642def create_volume_group(hostname, volume_group_name, disks=[], partitions=[]):
azvyagintsevbca1f462018-05-25 19:06:46 +0300643 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100644 Create new volume group on machine. Disks or partitions needs to be provided.
645
646 CLI Example:
647
648 .. code-block:: bash
649
650 salt 'maas-node' maasng.create_volume_group volume_group_name, disks=[sda,sdb], partitions=[]
651 salt-call maasng.create_volume_group server_hostname
azvyagintsevbca1f462018-05-25 19:06:46 +0300652 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100653 result = {}
654
655 data = {
656 "name": volume_group_name,
657 }
658
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200659 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100660 system_id = get_machine(hostname)["system_id"]
661 LOG.info(system_id)
662
663 disk_ids = []
664 partition_ids = []
665
666 for disk in disks:
667 p_disk = get_blockdevice(hostname, disk)
668 if p_disk["partition_table_type"] == None:
669 disk_ids.append(str(p_disk["id"]))
670 else:
azvyagintsevf3515c82018-06-26 18:59:05 +0300671 result["error"] = "Device {0} on" \
672 "machine {1} cointains partition" \
673 "table".format(disk, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100674 return result
675
676 for partition in partitions:
677 try:
678 device = partition.split("-")[0]
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200679 device_part = list_partitions(hostname, device)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100680 partition_ids.append(str(device_part[partition]["id"]))
681 except KeyError:
azvyagintsevf3515c82018-06-26 18:59:05 +0300682 result["error"] = "Partition {0} does" \
683 "not exists on " \
684 "machine {1}".format(partition, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100685 return result
686
687 data["block_devices"] = disk_ids
688 data["partitions"] = partition_ids
689 LOG.info(partition_ids)
690 LOG.info(partitions)
691
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200692 # TODO validation
693 json_res = json.loads(maas.post(
694 u"api/2.0/nodes/{0}/volume-groups/".format(system_id), None, **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100695 LOG.info(json_res)
696 result["new"] = "Volume group {0} created".format(json_res["name"])
697
698 return result
699
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200700
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100701def delete_volume_group(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300702 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100703 Delete volume group on machine.
704
705 CLI Example:
706
707 .. code-block:: bash
708
709 salt 'maas-node' maasng.delete_volume_group server_hostname vg0
710 salt-call maasng.delete_volume_group server_hostname vg0
azvyagintsevbca1f462018-05-25 19:06:46 +0300711 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100712
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200713 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100714 system_id = get_machine(hostname)["system_id"]
azvyagintsevbca1f462018-05-25 19:06:46 +0300715 LOG.debug('delete_volume_group:{}'.format(system_id))
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100716
azvyagintsevbca1f462018-05-25 19:06:46 +0300717 vg_id = str(_get_volume_group_id_by_name(hostname, name))
718 for vol in get_volumes(hostname, name):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200719 delete_volume(hostname, vol, name)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100720
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200721 # TODO validation
722 json_res = json.loads(maas.delete(
723 u"api/2.0/nodes/{0}/volume-group/{1}/".format(system_id, vg_id)).read() or 'null')
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100724 LOG.info(json_res)
725
726 return True
727
728
azvyagintsevf3515c82018-06-26 18:59:05 +0300729def create_volume(hostname, volume_name, volume_group, size, fs_type=None,
730 mount=None):
azvyagintsevbca1f462018-05-25 19:06:46 +0300731 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100732 Create volume on volume group.
733
734 CLI Example:
735
736 .. code-block:: bash
737
738 salt 'maas-node' maasng.create_volume server_hostname volume_name, volume_group, size, fs_type=None, mount=None
739 salt-call maasng.create_volume server_hostname volume_name, volume_group, size, fs_type=None, mount=None
azvyagintsevbca1f462018-05-25 19:06:46 +0300740 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100741
742 data = {
743 "name": volume_name,
744 }
745
746 value, unit = size[:-1], size[-1]
747 bit_size = str(int(value) * SIZE[unit])
748 LOG.info(bit_size)
749
750 data["size"] = bit_size
751
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200752 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100753 system_id = get_machine(hostname)["system_id"]
754 LOG.info(system_id)
755
756 volume_group_id = str(_get_volume_group_id_by_name(hostname, volume_group))
757
758 LOG.info(volume_group_id)
759
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200760 # TODO validation
761 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/volume-group/{1}/".format(
762 system_id, volume_group_id), "create_logical_volume", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100763 LOG.info(json_res)
764
765 if fs_type != None or mount != None:
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200766 ret = create_volume_filesystem(
767 hostname, volume_group + "-" + volume_name, fs_type, mount)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100768
769 return True
770
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100771
azvyagintsevbca1f462018-05-25 19:06:46 +0300772def delete_volume(hostname, volume_name, volume_group):
773 """
774 Delete volume from volume group.
775 Tips: maas always use 'volume_group-volume_name' name schema.Example: 'vg0-glusterfs'
776 This function expexts same format.
777
778 CLI Example:
779
780 .. code-block:: bash
781
782 salt 'maas-node' maasng.delete_volume server_hostname volume_name volume_group
783 salt 'maas-node' maasng.delete_volume server_hostname vg0-vol0 vg0
784 salt-call maasng.delete_volume server_hostname volume_name volume_group
785 """
786
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200787 maas = _create_maas_client()
azvyagintsevbca1f462018-05-25 19:06:46 +0300788 system_id = get_machine(hostname)["system_id"]
789 LOG.debug('delete_volume:{}'.format(system_id))
790
791 volume_group_id = str(_get_volume_group_id_by_name(hostname, volume_group))
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200792 volume_id = str(_get_volume_id_by_name(
793 hostname, volume_name, volume_group))
azvyagintsevbca1f462018-05-25 19:06:46 +0300794
795 if None in [volume_group_id, volume_id]:
796 return False
797
798 data = {
799 "id": volume_id,
800 }
801
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200802 # TODO validation
803 json_res = json.loads(maas.post(u"api/2.0/nodes/{0}/volume-group/{1}/".format(
804 system_id, volume_group_id), "delete_logical_volume", **data).read() or 'null')
azvyagintsevbca1f462018-05-25 19:06:46 +0300805 return True
806
807
808def get_volumes(hostname, vg_name):
809 """
810 Get list of volumes in volume group.
811 """
812 volumes = {}
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200813 _volumes = list_volume_groups(
814 hostname)[vg_name].get('logical_volumes', False)
azvyagintsevbca1f462018-05-25 19:06:46 +0300815 if _volumes:
816 for item in _volumes:
817 volumes[item["name"]] = item
818 return volumes
819
820# END LVM
821
822
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200823def create_volume_filesystem(hostname, device, fs_type=None, mount=None):
824
825 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100826 system_id = get_machine(hostname)["system_id"]
827
828 blockdevices_id = _get_blockdevice_id_by_name(hostname, device)
829 data = {}
830 if fs_type != None:
831 data["fstype"] = fs_type
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200832 # TODO validation
833 json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
834 system_id, blockdevices_id), "format", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100835 LOG.info(json_res)
836
837 if mount != None:
838 data["mount_point"] = mount
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200839 # TODO validation
840 json_res = json.loads(maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
841 system_id, blockdevices_id), "mount", **data).read())
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100842 LOG.info(json_res)
843
844 return True
845
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100846
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100847def set_boot_disk(hostname, name):
azvyagintsevbca1f462018-05-25 19:06:46 +0300848 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100849 Create volume on volume group.
850
851 CLI Example:
852
853 .. code-block:: bash
854
855 salt 'maas-node' maasng.set_boot_disk server_hostname disk_name
856 salt-call maasng.set_boot_disk server_hostname disk_name
azvyagintsevbca1f462018-05-25 19:06:46 +0300857 """
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100858 data = {}
859 result = {}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200860 maas = _create_maas_client()
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100861 system_id = get_machine(hostname)["system_id"]
862 blockdevices_id = _get_blockdevice_id_by_name(hostname, name)
863
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200864 maas.post(u"/api/2.0/nodes/{0}/blockdevices/{1}/".format(
865 system_id, blockdevices_id), "set_boot_disk", **data).read()
azvyagintsevf3515c82018-06-26 18:59:05 +0300866 # TODO validation for error response
867 # (disk does not exists and node does not exists)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100868 result["new"] = "Disk {0} was set as bootable".format(name)
869
870 return result
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200871
azvyagintsevbca1f462018-05-25 19:06:46 +0300872# NETWORKING
873
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200874
875def list_fabric():
azvyagintsevbca1f462018-05-25 19:06:46 +0300876 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200877 Get list of all fabric
878
879 CLI Example:
880
881 .. code-block:: bash
882
883 salt 'maas-node' maasng.list_fabric
azvyagintsevbca1f462018-05-25 19:06:46 +0300884 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200885 fabrics = {}
886 maas = _create_maas_client()
887 json_res = json.loads(maas.get(u'api/2.0/fabrics/').read())
888 LOG.info(json_res)
889 for item in json_res:
890 fabrics[item["name"]] = item
891 return fabrics
892
893
azvyagintsevf3515c82018-06-26 18:59:05 +0300894def check_fabric(name):
895 """
896 Simple check that fabric already defined
897 Return format:
898 update - require update
899 correct - fully coincides # not implemented
900 not_exist - need's to be created
901 """
902
903 ret = 'not_exist'
904 fabrics = list_fabric()
905 if name in fabrics.keys():
906 LOG.debug("Requested fabrics with name:{} already exist".format(name))
907 ret = 'update'
908 return ret
909
910
911def check_fabric_guess_with_cidr(name, cidrs):
912 """
913 Check, that fabric already defined OR it was autodiscovered
914 WA to fix issue with hardcoded 'fabric-0'
915 - Find all auto-discovered subnets by cidr
916 - find all subnets, that SHOULD be configured to THIS subent
917 Warning: most probably, will fail if some subnet defined
918 to another fabric :(
919
920 { 'update' : ID } - require update
921 { 'correct' : ID } - fully coincides # not implemented
922 { 'not_exist' : None } - need's to be created
923
924 CLI Example:
925
926 .. code-block:: bash
927
928 salt 'maas-node' maasng.check_fabric_guess_with_cidr name='' cidrs=[]
929 """
930
931 ret = {'not_exist': None}
932 fabrics = list_fabric()
933 # Simple check
934 if name in fabrics:
935 LOG.debug("Requested fabrics with name:{} already exist".format(name))
936 f_id = fabrics[name]['id']
937 ret = {'update': f_id}
938 # Cidr check
939 # All discovered subnets by cidr
940 d_subnets = list_subnets(sort_by='cidr')
941 # Check, that requested cidr already in discovered.
942 # If it is - it would mean that fabric already
943 # exist(fabric-0,most probably) but should be renamed.
944 # Works only for first shot ;(
945 # due curren-single-maas logic for 'vlan-subnet' mapping.
946 # Probably, it will fail with future MAAS releases.
947 for cidr in cidrs:
948 if cidr in d_subnets:
949 f_id = d_subnets[cidr]['vlan']['fabric_id']
950 f_name = d_subnets[cidr]['vlan']['fabric']
951 LOG.warning("Detected cidr:{} in fabric:{}".format(cidr, f_name))
952 LOG.warning("Guessing, that fabric "
953 "with current name:{}\n should be "
954 "renamed to:{}".format(f_name, name))
955 ret = {'update': f_id}
956 return ret
957 return ret
958
959
azvyagintsevf0904ac2018-07-05 18:53:26 +0300960def create_fabric(name, description=None, fabric_id=None, update=False):
azvyagintsevbca1f462018-05-25 19:06:46 +0300961 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200962 Create new fabric.
963
964 CLI Example:
965
966 .. code-block:: bash
967
azvyagintsevf3515c82018-06-26 18:59:05 +0300968 salt 'maas-node' maasng.create_fabric name='123'
azvyagintsevbca1f462018-05-25 19:06:46 +0300969 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200970 result = {}
971 data = {
972 "name": name,
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200973 "class_type": '',
974
975 }
azvyagintsevf3515c82018-06-26 18:59:05 +0300976 if description:
977 data['description'] = description
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200978
979 maas = _create_maas_client()
azvyagintsevf0904ac2018-07-05 18:53:26 +0300980 json_res = None
azvyagintsevf3515c82018-06-26 18:59:05 +0300981 try:
982 if update:
983 json_res = json.loads(
984 maas.put(u"api/2.0/fabrics/{0}/".format(fabric_id),
985 **data).read())
986 result["new"] = "Fabric {0} created".format(json_res["name"])
987 else:
988 json_res = json.loads(
989 maas.post(u"api/2.0/fabrics/", None, **data).read())
990 result["changes"] = "Fabric {0} updated".format(json_res["name"])
991 except Exception as inst:
992 LOG.debug("create_fabric data:{}".format(data))
993 try:
994 m = inst.readlines()
995 except:
996 m = inst.message
997 LOG.error("Message:{0}".format(m))
998 result['result'] = False
999 result['comment'] = 'Error creating fabric: {0}'.format(name)
1000 result['error'] = m
1001 return result
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001002 LOG.debug("crete_fabric:{}".format(json_res))
azvyagintsevf3515c82018-06-26 18:59:05 +03001003 result['result'] = True
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001004 return result
1005
1006
azvyagintsevf3515c82018-06-26 18:59:05 +03001007def list_subnets(sort_by='name'):
azvyagintsevbca1f462018-05-25 19:06:46 +03001008 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001009 Get list of subnets from maas server
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001010
1011 CLI Example:
1012
1013 .. code-block:: bash
1014
azvyagintsevf3515c82018-06-26 18:59:05 +03001015 salt 'maas-node' maasng.list_subnets
azvyagintsevbca1f462018-05-25 19:06:46 +03001016 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001017 subnets = {}
1018 maas = _create_maas_client()
1019 json_res = json.loads(maas.get(u'api/2.0/subnets/').read())
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001020 for item in json_res:
azvyagintsevf3515c82018-06-26 18:59:05 +03001021 subnets[item[sort_by]] = item
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001022 return subnets
1023
1024
azvyagintsevf3515c82018-06-26 18:59:05 +03001025def list_vlans(fabric, sort_by='vid'):
azvyagintsevbca1f462018-05-25 19:06:46 +03001026 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001027 Get list of vlans in fabric
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001028
1029 CLI Example:
1030
1031 .. code-block:: bash
1032
azvyagintsevf3515c82018-06-26 18:59:05 +03001033 salt 'maas-node' maasng.list_vlans fabric_name
azvyagintsevbca1f462018-05-25 19:06:46 +03001034 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001035 vlans = {}
1036 maas = _create_maas_client()
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001037 fabric_id = get_fabricid(fabric)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001038
azvyagintsevf3515c82018-06-26 18:59:05 +03001039 try:
1040 json_res = json.loads(
1041 maas.get(u'api/2.0/fabrics/{0}/vlans/'.format(fabric_id)).read())
1042 except Exception as inst:
1043 m = inst.readlines()
1044 LOG.error("Message:{0}".format(m))
1045 LOG.debug(json_res)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001046 for item in json_res:
azvyagintsevf3515c82018-06-26 18:59:05 +03001047 vlans[item[sort_by]] = item
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001048 return vlans
1049
1050
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001051def get_fabricid(fabric):
azvyagintsevbca1f462018-05-25 19:06:46 +03001052 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001053 Get id for specific fabric
1054
1055 CLI Example:
1056
1057 .. code-block:: bash
1058
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001059 salt 'maas-node' maasng.get_fabricid fabric_name
azvyagintsevbca1f462018-05-25 19:06:46 +03001060 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001061 try:
1062 return list_fabric()[fabric]['id']
1063 except KeyError:
azvyagintsevefb6f5d2018-07-10 14:16:19 +03001064 return {"error": "Fabric not found on MaaS server"}
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001065
1066
azvyagintsevf3515c82018-06-26 18:59:05 +03001067def check_vlan_in_fabric(fabric, vlan):
1068 """
1069 Check that VLAN exactly defined
1070 Return format:
1071 update - require update
1072 correct - fully coincides # not implemented
1073 not_exist - need's to be created
1074 """
1075
1076 ret = 'not_exist'
1077 vlans = list_vlans(fabric)
1078 if vlan in vlans.keys():
1079 LOG.debug("Requested VLAN:{} already exist"
1080 "in FABRIC:{}".format(vlan, fabric))
1081 ret = 'update'
1082 return ret
1083
1084
1085def create_vlan_in_fabric(name, fabric, vlan, description, primary_rack,
1086 dhcp_on=False, update=False, vlan_id=""):
azvyagintsevbca1f462018-05-25 19:06:46 +03001087 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001088 Update vlan
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001089 CLI Example:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001090 .. code-block:: bash
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001091 salt 'maas-node' maasng.update_vlan name, fabric, vid, description, dhcp_on
azvyagintsevbca1f462018-05-25 19:06:46 +03001092 """
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001093 result = {}
1094
1095 data = {
1096 "name": name,
1097 "dhcp_on": str(dhcp_on),
1098 "description": description,
azvyagintsev6913e5e2018-07-05 11:42:53 +03001099 "primary_rack": list_racks()[primary_rack]['system_id'],
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001100 }
azvyagintsevf3515c82018-06-26 18:59:05 +03001101 vlan = str(vlan)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001102 maas = _create_maas_client()
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001103 fabric_id = get_fabricid(fabric)
azvyagintsevf3515c82018-06-26 18:59:05 +03001104 try:
1105 if update:
1106 # MAAS have buggy logic here. Fallowing api reference, here should
1107 # be passed VID - which mean, API ID for vlan.
1108 # Otherwise, at least for maas 2.3.3-6498-ge4db91d exactly VLAN
1109 # should be passed. so, make temp.backward-convertation.
1110 # json_res = json.loads(maas.put(u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id,vlan_id), **data).read())
1111 json_res = json.loads(maas.put(
1112 u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id, vlan),
1113 **data).read())
1114 else:
1115 data['vid'] = str(vlan)
1116 json_res = json.loads(maas.post(u'api/2.0/fabrics/{0}/vlans/'.format(fabric_id), None, **data).read())
1117 except Exception as inst:
1118 LOG.debug("create_vlan_in_fabric data:{}".format(data))
1119 try:
1120 m = inst.readlines()
1121 except:
1122 m = inst.message
1123 LOG.error("Message:{0}".format(m))
1124 result['result'] = False
1125 result['comment'] = 'Error updating vlan: {0}'.format(name)
1126 result['error'] = m
1127 return result
1128 LOG.debug("create_vlan_in_fabric:{}".format(json_res))
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001129 result["new"] = "Vlan {0} was updated".format(json_res["name"])
1130
1131 return result
azvyagintsevbca1f462018-05-25 19:06:46 +03001132
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001133
azvyagintsevf3515c82018-06-26 18:59:05 +03001134def check_subnet(cidr, name, fabric, gateway_ip):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001135 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001136 Check that subnet exactly defined
1137 Return format:
1138 update - require update
1139 correct - fully coincides # not implemented
1140 not_exist - need's to be created
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001141 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001142
1143 ret = 'not_exist'
1144 subnets = list_subnets(sort_by='cidr')
1145 if cidr in subnets.keys():
1146 LOG.debug("Requested subnet cidr:{} already exist".format(cidr))
1147 ret = 'update'
1148 return ret
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001149
1150
azvyagintsevf3515c82018-06-26 18:59:05 +03001151def create_subnet(cidr='', name='', fabric='', gateway_ip='', vlan='',
1152 update=False, subnet_id=''):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001153 """
1154 Create subnet
1155
1156 CLI Example:
1157
1158 .. code-block:: bash
1159
1160 salt 'maas-node' maasng.create_subnet cidr, name, fabric, gateway_ip
1161 """
1162
1163 fabric_id = get_fabricid(fabric)
1164 result = {}
azvyagintsevf3515c82018-06-26 18:59:05 +03001165 vlan=str(vlan)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001166 data = {
1167 "cidr": cidr,
1168 "name": name,
1169 "fabric": str(fabric_id),
1170 "gateway_ip": gateway_ip,
azvyagintsevf3515c82018-06-26 18:59:05 +03001171 'vlan': vlan,
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001172 }
1173 maas = _create_maas_client()
azvyagintsevf3515c82018-06-26 18:59:05 +03001174 # FIXME: vlan definition not work in 2.3.3-6498-ge4db91d.
1175 LOG.warning("Ignoring parameter vlan:{}".format(vlan))
1176 data.pop('vlan','')
1177 try:
1178 if update:
1179 json_res = json.loads(maas.put(u"api/2.0/subnets/{0}/".format(subnet_id), **data).read())
1180 else:
1181 json_res = json.loads(maas.post(u"api/2.0/subnets/", None, **data).read())
1182 except Exception as inst:
1183 LOG.debug("create_subnet data:{}".format(data))
1184 try:
1185 m = inst.readlines()
1186 except:
1187 m = inst.message
1188 LOG.error("Message:{0}".format(m))
1189 result['result'] = False
1190 result['comment'] = 'Error creating subnet: {0}'.format(name)
1191 result['error'] = m
1192 return result
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001193 LOG.debug("create_subnet:{}".format(json_res))
azvyagintsevf3515c82018-06-26 18:59:05 +03001194 result["new"] = "Subnet {0} with CIDR {1}" \
1195 "and gateway {2} was created".format(name, cidr, gateway_ip)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001196
1197 return result
1198
1199
1200def get_subnet(subnet):
1201 """
1202 Get details for specific subnet
1203
1204 CLI Example:
1205
1206 .. code-block:: bash
1207
1208 salt 'maas-node' maasng.get_subnet subnet_name
1209 """
1210 try:
azvyagintsevf3515c82018-06-26 18:59:05 +03001211 return list_subnets()[subnet]
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001212 except KeyError:
1213 return {"error": "Subnet not found on MaaS server"}
1214
1215
1216def get_subnetid(subnet):
1217 """
1218 Get id for specific subnet
1219
1220 CLI Example:
1221
1222 .. code-block:: bash
1223
1224 salt 'maas-node' maasng.get_subnetid subnet_name
1225 """
1226 try:
azvyagintsevf3515c82018-06-26 18:59:05 +03001227 return list_subnets()[subnet]['id']
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001228 except KeyError:
1229 return {"error": "Subnet not found on MaaS server"}
1230
1231
1232def list_ipranges():
1233 """
1234 Get list of all ipranges from maas server
1235
1236 CLI Example:
1237
1238 .. code-block:: bash
1239
1240 salt 'maas-node' maasng.list_ipranges
1241 """
1242 ipranges = {}
1243 maas = _create_maas_client()
1244 json_res = json.loads(maas.get(u'api/2.0/ipranges/').read())
1245 for item in json_res:
1246 ipranges[item["start_ip"]] = item
1247 return ipranges
1248
1249
azvyagintsevf0904ac2018-07-05 18:53:26 +03001250def create_iprange(type_range, start_ip, end_ip, subnet=None, comment=None):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001251 """
1252 Create ip range
1253
1254 CLI Example:
1255
1256 .. code-block:: bash
1257
1258 salt 'maas-node' maasng.create_iprange type, start ip, end ip, comment
1259 """
1260 result = {}
1261
1262 data = {
1263 "type": type_range,
1264 "start_ip": start_ip,
1265 "end_ip": end_ip,
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001266 }
azvyagintsevf3515c82018-06-26 18:59:05 +03001267 if comment:
1268 data['comment'] = comment
azvyagintsevf0904ac2018-07-05 18:53:26 +03001269 if subnet:
1270 subnet_id = list_subnets()[subnet]['id']
1271 data['subnet'] = str(subnet_id)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001272 maas = _create_maas_client()
azvyagintsevf3515c82018-06-26 18:59:05 +03001273 _name = "Type:{}: {}-{}".format(type_range, start_ip, end_ip)
1274 try:
1275 json_res = json.loads(
1276 maas.post(u"api/2.0/ipranges/", None, **data).read())
1277 except Exception as inst:
1278 try:
1279 m = inst.readlines()
1280 except:
1281 m = inst.message
1282 LOG.error("Message:{0}".format(m))
1283 result['result'] = False
1284 result['comment'] = 'Error creating iprange:{0}'.format(_name)
1285 result['error'] = m
1286 return result
1287 result["new"] = "Iprange: {0} has been created".format(_name)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001288 LOG.debug("create_iprange:{}".format(json_res))
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001289
1290 return result
1291
1292
1293def get_iprangeid(start_ip):
1294 """
1295 Get id for ip range from maas server
1296
1297 CLI Example:
1298
1299 .. code-block:: bash
1300
1301 salt 'maas-node' maasng.get_iprangeid start_ip
1302 """
1303 try:
1304 return list_ipranges()[start_ip]['id']
1305 except KeyError:
1306 return {"error": "Ip range not found on MaaS server"}
1307
1308
1309def get_startip(start_ip):
1310 """
1311 Get start ip for ip range
1312
1313 CLI Example:
1314
1315 .. code-block:: bash
1316
1317 salt 'maas-node' maasng.get_startip start ip
1318 """
1319 try:
1320 return list_ipranges()[start_ip]
1321 except KeyError:
1322 return {"error": "Ip range not found on MaaS server"}
azvyagintsevbca1f462018-05-25 19:06:46 +03001323# END NETWORKING
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001324
1325# MAAS CONFIG SECTION
1326
1327
azvyagintsev58947072018-06-29 12:09:48 +03001328def _getHTTPCode(url):
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001329 code = '003'
1330 m = ''
azvyagintsev58947072018-06-29 12:09:48 +03001331 try:
1332 connection = urllib2.urlopen(url)
1333 code = connection.getcode()
1334 connection.close()
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001335 except (urllib2.HTTPError, urllib2.URLError) as e:
1336 try:
1337 code = e.getcode()
1338 except:
1339 m = e.reason
1340 pass
azvyagintsevf3515c82018-06-26 18:59:05 +03001341 LOG.debug("Unexpected http code:{} from "
1342 "url:{}\nwith message:{}".format(code, url, m))
azvyagintsev58947072018-06-29 12:09:48 +03001343 pass
1344 return code
1345
1346
1347def wait_for_http_code(url=None, expected=[200]):
1348 """
1349 Simple function, which just wait for avaible api, aka wait for 200.
1350
1351 CLI Example:
1352
1353 .. code-block:: bash
1354
1355 salt 'maas-node' maasng.wait_for_http_code url expected=[200]
1356
1357 """
1358 ret = {}
1359 started_at = time.time()
1360 poll_time = 5
1361 timeout = 60 * 2
1362 while _getHTTPCode(url) not in expected:
1363 c_timeout = timeout - (time.time() - started_at)
1364 if c_timeout <= 0:
1365 ret['result'] = False
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001366 ret["comment"] = "api:{} not answered in time".format(url)
azvyagintsev58947072018-06-29 12:09:48 +03001367 return ret
1368 LOG.info(
1369 "Waiting for api:{0}\n"
1370 "sleep for:{1}s "
1371 "Left:{2}/{3}s".format(url, poll_time, round(c_timeout),
1372 timeout))
1373 time.sleep(poll_time)
1374 ret['result'] = True
1375 ret["comment"] = "MAAS API:{} up.".format(url)
1376 return ret
1377
1378
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001379def _get_boot_source_id_by_url(url):
1380 # FIXME: fix ret\validation
1381 try:
1382 bs_id = get_boot_source(url=url)["id"]
1383 except KeyError:
1384 return {"error": "boot-source:{0} not exist!".format(url)}
1385 return bs_id
1386
1387
1388def get_boot_source(url=None):
1389 """
1390 Read a boot source by url. If url not specified - return all.
1391
1392 CLI Example:
1393
1394 .. code-block:: bash
1395
1396 salt 'maas-node' maasng.get_boot_source url
1397
1398 """
1399 boot_sources = {}
1400 maas = _create_maas_client()
1401 json_res = json.loads(maas.get(u'api/2.0/boot-sources/').read() or 'null')
1402 for item in json_res:
1403 boot_sources[str(item["url"])] = item
1404 if url:
1405 return boot_sources.get(url, {})
1406 return boot_sources
1407
1408
1409def delete_boot_source(url, bs_id=None):
1410 """
1411 Delete a boot source by url.
1412
1413 CLI Example:
1414
1415 .. code-block:: bash
1416
1417 sal 'maas-node' maasng.delete url
1418
1419 """
1420 result = {}
1421 if not bs_id:
1422 bs_id = _get_boot_source_id_by_url(url)
1423 maas = _create_maas_client()
1424 json_res = json.loads(maas.delete(
1425 u'/api/2.0/boot-sources/{0}/'.format(bs_id)).read() or 'null')
1426 LOG.debug("delete_boot_source:{}".format(json_res))
1427 result["new"] = "Boot-resource {0} deleted".format(url)
1428 return result
1429
1430
1431def boot_sources_delete_all_others(except_urls=[]):
1432 """
1433 Delete all boot-sources, except defined in 'except_urls' list.
1434 """
1435 result = {}
1436 maas_boot_sources = get_boot_source()
1437 if 0 in [len(except_urls), len(maas_boot_sources)]:
1438 result['result'] = None
1439 result[
1440 "comment"] = "Exclude or maas sources for delete empty. No changes goinng to be."
1441 return result
1442 for url in maas_boot_sources.keys():
1443 if url not in except_urls:
1444 LOG.info("Removing boot-source:{}".format(url))
1445 boot_resources_import(action='stop_import', wait=True)
1446 result["changes"] = delete_boot_source(url)
1447 return result
1448
1449
1450def create_boot_source(url, keyring_filename='', keyring_data='', wait=False):
1451 """
1452 Create and import maas boot-source: link to maas-ephemeral repo
1453 Be aware, those step will import resource to rack ctrl, but you also need to import
1454 them into the region!
1455
1456
1457 :param url: The URL of the BootSource.
1458 :param keyring_filename: The path to the keyring file for this BootSource.
1459 :param keyring_data: The GPG keyring for this BootSource, base64-encoded data.
1460
1461 """
1462
1463 # TODO: not work with 'update' currently => keyring update may fail.
1464 result = {}
1465
1466 data = {
1467 "url": url,
1468 "keyring_filename": keyring_filename,
1469 "keyring_data": str(keyring_data),
1470 }
1471
1472 maas = _create_maas_client()
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001473 if url in get_boot_source():
1474 result['result'] = None
1475 result["comment"] = "boot resource already exist"
1476 return result
1477
1478 # NOTE: maas.post will return 400, if url already defined.
1479 json_res = json.loads(
1480 maas.post(u'api/2.0/boot-sources/', None, **data).read())
1481 if wait:
1482 LOG.debug(
1483 "Sleep for 5s,to get MaaS some time to process previous request")
1484 time.sleep(5)
1485 ret = boot_resources_is_importing(wait=True)
1486 if ret is dict:
1487 return ret
1488 LOG.debug("create_boot_source:{}".format(json_res))
1489 result["new"] = "boot resource {0} was created".format(json_res["url"])
1490
1491 return result
1492
1493
1494def boot_resources_import(action='import', wait=False):
1495 """
1496 import/stop_import the boot resources.
1497
1498 :param action: import\stop_import
1499 :param wait: True\False. Wait till process finished.
1500
1501 CLI Example:
1502
1503 .. code-block:: bash
1504
1505 salt 'maas-node' maasng.boot_resources_import action='import'
1506
1507 """
1508 maas = _create_maas_client()
1509 # Have no idea why, but usual jsonloads not work here..
1510 imp = maas.post(u'api/2.0/boot-resources/', action)
1511 if imp.code == 200:
1512 LOG.debug('boot_resources_import:{}'.format(imp.readline()))
1513 if wait:
1514 boot_resources_is_importing(wait=True)
1515 return True
1516 else:
1517 return False
1518
1519
1520def boot_resources_is_importing(wait=False):
1521 maas = _create_maas_client()
1522 result = {}
1523 if wait:
1524 started_at = time.time()
1525 poll_time = 5
1526 timeout = 60 * 15
1527 while boot_resources_is_importing(wait=False):
1528 c_timeout = timeout - (time.time() - started_at)
1529 if c_timeout <= 0:
1530 result['result'] = False
1531 result["comment"] = "Boot-resources import not finished in time"
1532 return result
1533 LOG.info(
1534 "Waiting boot-resources import done\n"
1535 "sleep for:{}s "
1536 "Left:{}/{}s".format(poll_time, round(c_timeout), timeout))
1537 time.sleep(poll_time)
1538 return json.loads(
1539 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1540 else:
1541 return json.loads(
1542 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1543
1544#####
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001545# def boot_sources_selections_delete_all_others(except_urls=[]):
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001546# """
1547# """
1548# result = {}
1549# return result
1550
1551
1552def is_boot_source_selections_in(dict1, list1):
1553 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001554 Check that requested boot-selection already in maas bs selections,
1555 if True- return bss id.
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001556 # FIXME: those hack check doesn't look good.
1557 """
1558 for bs in list1:
1559 same = set(dict1.keys()) & set(bs.keys())
1560 if all(elem in same for elem in
1561 ['os', 'release', 'arches', 'subarches', 'labels']):
azvyagintsevf3515c82018-06-26 18:59:05 +03001562 LOG.debug("boot-selection in maas:{0}\n"
1563 "looks same to requested:{1}".format(bs, dict1))
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001564 return bs['id']
1565 return False
1566
1567
1568def get_boot_source_selections(bs_url):
1569 """
1570 Get boot-source selections.
1571 """
1572 # check for key_error!
1573 bs_id = _get_boot_source_id_by_url(bs_url)
1574 maas = _create_maas_client()
1575 json_res = json.loads(
1576 maas.get(u'/api/2.0/boot-sources/{0}/selections/'.format(bs_id)).read())
1577 LOG.debug(
1578 "get_boot_source_selections for url:{} \n{}".format(bs_url, json_res))
1579 return json_res
1580
1581
1582def create_boot_source_selections(bs_url, os, release, arches="*",
1583 subarches="*", labels="*", wait=True):
1584 """
1585 Create a new boot source selection for bs_url.
1586 :param os: The OS (e.g. ubuntu, centos) for which to import resources.Required.
1587 :param release: The release for which to import resources. Required.
1588 :param arches: The architecture list for which to import resources.
1589 :param subarches: The subarchitecture list for which to import resources.
1590 :param labels: The label lists for which to import resources.
1591 """
1592
azvyagintsevcb54d142018-06-19 16:18:32 +03001593 result = { "result" : True, 'name' : bs_url, 'changes' : None }
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001594
1595 data = {
1596 "os": os,
1597 "release": release,
1598 "arches": arches,
1599 "subarches": subarches,
1600 "labels": labels,
1601 }
1602
1603 maas = _create_maas_client()
1604 bs_id = _get_boot_source_id_by_url(bs_url)
1605 # TODO add pre-create verify
1606 maas_bs_s = get_boot_source_selections(bs_url)
1607 if is_boot_source_selections_in(data, maas_bs_s):
1608 result["result"] = True
azvyagintsevf3515c82018-06-26 18:59:05 +03001609 result["comment"] = 'Requested boot-source selection ' \
1610 'for {0} already exist.'.format(
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001611 bs_url)
1612 return result
1613
1614 # NOTE: maas.post will return 400, if url already defined.
azvyagintsevcb54d142018-06-19 16:18:32 +03001615 # Also, maas need's some time to import info about stream.
1616 # unfortunatly, maas don't have any call to check stream-import-info - so, we need to implement
1617 # at least simple retry ;(
1618 json_res = False
1619 poll_time = 5
1620 for i in range(0,5):
1621 try:
1622 json_res = json.loads(
1623 maas.post(u'api/2.0/boot-sources/{0}/selections/'.format(bs_id), None,
1624 **data).read())
1625 except Exception as inst:
1626 m = inst.readlines()
azvyagintsevf3515c82018-06-26 18:59:05 +03001627 LOG.warning("boot_source_selections "
1628 "catch error during processing. Most-probably, "
1629 "streams not imported yet.\nSleep:{}s"
1630 "Retry:{}/5".format(poll_time, i))
azvyagintsevcb54d142018-06-19 16:18:32 +03001631 LOG.warning("Message:{0}".format(m))
1632 time.sleep(poll_time)
1633 continue
1634 break
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001635 LOG.debug("create_boot_source_selections:{}".format(json_res))
azvyagintsevcb54d142018-06-19 16:18:32 +03001636 if not json_res:
1637 result["result"] = False
azvyagintsevf3515c82018-06-26 18:59:05 +03001638 result["comment"] = 'Failed to create requested boot-source selection' \
1639 ' for {0}.'.format(bs_url)
azvyagintsevcb54d142018-06-19 16:18:32 +03001640 return result
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001641 if wait:
1642 LOG.debug(
1643 "Sleep for 5s,to get MaaS some time to process previous request")
1644 time.sleep(5)
1645 ret = boot_resources_import(action='import', wait=True)
1646 if ret is dict:
1647 return ret
azvyagintsevcb54d142018-06-19 16:18:32 +03001648 result["comment"] = "boot-source selection for {0} was created".format(bs_url)
1649 result["new"] = data
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001650
1651 return result
1652
1653# END MAAS CONFIG SECTION
azvyagintsevcb54d142018-06-19 16:18:32 +03001654
1655# RACK CONTROLLERS SECTION
1656
1657
1658def get_rack(hostname):
1659 """
1660 Get information about specified rackd
1661
1662 CLI Example:
1663
1664 .. code-block:: bash
1665
1666 salt-call maasng.get_rack rack_hostname
1667 """
1668 try:
1669 return list_racks()[hostname]
1670 except KeyError:
1671 return {"error": "rack:{} not found on MaaS server".format(hostname)}
1672
1673
azvyagintsev6913e5e2018-07-05 11:42:53 +03001674def list_racks(sort_by='hostname'):
azvyagintsevcb54d142018-06-19 16:18:32 +03001675 """
1676 Get list of all rack controllers from maas server
1677
1678 CLI Example:
1679
1680 .. code-block:: bash
1681
1682 salt-call maasng.list_racks
1683 """
1684 racks = {}
1685 maas = _create_maas_client()
1686 json_res = json.loads(
1687 maas.get(u"/api/2.0/rackcontrollers/").read() or 'null')
1688 for item in json_res:
azvyagintsev6913e5e2018-07-05 11:42:53 +03001689 racks[item[sort_by]] = item
azvyagintsevcb54d142018-06-19 16:18:32 +03001690 return racks
1691
1692
1693def sync_bs_to_rack(hostname=None):
1694 """
1695 Sync RACK boot-sources with REGION. If no hostname probided - sync to all.
1696
1697 CLI Example:
1698
1699 .. code-block:: bash
1700
1701 salt-call maasng.sync_bs_to_rack rack_hostname
1702 """
1703 ret = {}
1704 maas = _create_maas_client()
1705 if not hostname:
1706 LOG.info("boot-sources sync initiated for ALL Rack's")
1707 # Convert to json-like format
1708 json_res = json.loads('["{0}"]'.format(
1709 maas.post(u"/api/2.0/rackcontrollers/",
1710 'import_boot_images').read()))
1711 LOG.debug("sync_bs_to_rack:{}".format(json_res))
1712 ret['result'] = True
1713 ret['comment'] = "boot-sources sync initiated for ALL Rack's"
1714 return ret
1715 LOG.info("boot-sources sync initiated for RACK:{0}".format(hostname))
1716 # Convert to json-like format
1717 json_res = json.loads('["{0}"]'.format(maas.post(
1718 u"/api/2.0/rackcontrollers/{0}/".format(
1719 get_rack(hostname)['system_id']),
1720 'import_boot_images').read()))
1721 LOG.debug("sync_bs_to_rack:{}".format(json_res))
1722 ret['result'] = True
1723 ret['comment'] = "boot-sources sync initiated for {0} Rack's".format(
1724 hostname)
1725 return
1726
1727
1728def rack_list_boot_imgs(hostname):
1729 ret = {}
1730 maas = _create_maas_client()
1731 LOG.debug("rack_list_boot_imgs:{}".format(hostname))
1732 ret = json.loads(maas.get(u"/api/2.0/rackcontrollers/{0}/".format(
1733 get_rack(hostname)['system_id']), 'list_boot_images').read() or 'null')
1734 return ret
1735
1736
1737def is_rack_synced(hostname):
1738 rez = rack_list_boot_imgs(hostname)['status']
1739 if rez == 'synced':
1740 return True
1741 return False
1742
1743# TODO do we actually need _exact_ check per-pack?
1744# def wait_for_images_on_rack(hostname):
1745#
1746# """
1747# WA function, to be able check that RACK actually done SYNC images
1748# for REQUIRED images at least.
1749# Required image to be fetched from
1750# reclass:maas:region:boot_sources_selections:[keys]:os/release formation
1751#
1752# CLI Example:
1753#
1754# .. code-block:: bash
1755#
1756# salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
1757# """
1758# try:
1759# bss = __salt__['config.get']('maas')['region']['boot_sources_selections']
1760# except KeyError:
1761# ret['result'] = None
1762# ret['comment'] = "boot_sources_selections definition for sync not found."
1763# return ret
1764# s_names = []
1765# # Format u'name': u'ubuntu/xenial'
1766# for v in bss.values():s_names.append("{0}/{1}".format(v['os'],v['release']))
1767# # Each names, should be in rack and whole rack should be in sync-ed state
1768
1769
1770def sync_and_wait_bs_to_all_racks():
1771 """
1772 Sync ALL rack's with regions source images.
1773
1774 CLI Example:
1775
1776 .. code-block:: bash
1777
1778 salt-call maasng.sync_and_wait_bs_to_all_racks
1779 """
1780 sync_bs_to_rack()
1781 for rack in list_racks().keys():
1782 wait_for_sync_bs_to_rack(hostname=rack)
1783 return True
1784
1785
1786def wait_for_sync_bs_to_rack(hostname=None):
1787 """
1788 Wait for boot images sync finished, on exact rack
1789
1790 CLI Example:
1791
1792 .. code-block:: bash
1793
1794 salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
1795 """
1796 ret = {}
1797 started_at = time.time()
1798 poll_time = 5
1799 timeout = 60 * 15
1800 while not is_rack_synced(hostname):
1801 c_timeout = timeout - (time.time() - started_at)
1802 if c_timeout <= 0:
1803 ret['result'] = False
1804 ret[
1805 "comment"] = "Boot-resources sync on rackd:{0}" \
1806 "not finished in time".format(
1807 hostname)
1808 return ret
1809 LOG.info(
1810 "Waiting boot-resources sync done to rack:{0}\n"
1811 "sleep for:{1}s "
1812 "Left:{2}/{3}s".format(hostname, poll_time, round(c_timeout),
1813 timeout))
1814 time.sleep(poll_time)
1815 ret['result'] = is_rack_synced(hostname)
1816 ret["comment"] = "Boot-resources sync on rackd:{0} finished".format(
1817 hostname)
1818 return ret
1819
1820# END RACK CONTROLLERS SECTION