blob: c08d5db6fc864df94ea96188d0c24efc6cf4fa75 [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
Petr Ruzicka80471852018-07-13 14:08:27 +02001085def create_vlan_in_fabric(name, fabric, vlan, description, primary_rack, mtu=1500,
azvyagintsevf3515c82018-06-26 18:59:05 +03001086 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 }
Michael Polenchukd25da792018-07-19 18:27:11 +04001101 if mtu:
1102 data['mtu'] = str(mtu)
azvyagintsevf3515c82018-06-26 18:59:05 +03001103 vlan = str(vlan)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001104 maas = _create_maas_client()
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001105 fabric_id = get_fabricid(fabric)
azvyagintsevf3515c82018-06-26 18:59:05 +03001106 try:
1107 if update:
1108 # MAAS have buggy logic here. Fallowing api reference, here should
1109 # be passed VID - which mean, API ID for vlan.
1110 # Otherwise, at least for maas 2.3.3-6498-ge4db91d exactly VLAN
1111 # should be passed. so, make temp.backward-convertation.
1112 # json_res = json.loads(maas.put(u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id,vlan_id), **data).read())
1113 json_res = json.loads(maas.put(
1114 u'api/2.0/fabrics/{0}/vlans/{1}/'.format(fabric_id, vlan),
1115 **data).read())
1116 else:
1117 data['vid'] = str(vlan)
1118 json_res = json.loads(maas.post(u'api/2.0/fabrics/{0}/vlans/'.format(fabric_id), None, **data).read())
1119 except Exception as inst:
1120 LOG.debug("create_vlan_in_fabric data:{}".format(data))
1121 try:
1122 m = inst.readlines()
1123 except:
1124 m = inst.message
1125 LOG.error("Message:{0}".format(m))
1126 result['result'] = False
1127 result['comment'] = 'Error updating vlan: {0}'.format(name)
1128 result['error'] = m
1129 return result
1130 LOG.debug("create_vlan_in_fabric:{}".format(json_res))
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001131 result["new"] = "Vlan {0} was updated".format(json_res["name"])
1132
1133 return result
azvyagintsevbca1f462018-05-25 19:06:46 +03001134
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001135
azvyagintsevf3515c82018-06-26 18:59:05 +03001136def check_subnet(cidr, name, fabric, gateway_ip):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001137 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001138 Check that subnet exactly defined
1139 Return format:
1140 update - require update
1141 correct - fully coincides # not implemented
1142 not_exist - need's to be created
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001143 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001144
1145 ret = 'not_exist'
1146 subnets = list_subnets(sort_by='cidr')
1147 if cidr in subnets.keys():
1148 LOG.debug("Requested subnet cidr:{} already exist".format(cidr))
1149 ret = 'update'
1150 return ret
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001151
1152
azvyagintsevf3515c82018-06-26 18:59:05 +03001153def create_subnet(cidr='', name='', fabric='', gateway_ip='', vlan='',
1154 update=False, subnet_id=''):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001155 """
1156 Create subnet
1157
1158 CLI Example:
1159
1160 .. code-block:: bash
1161
1162 salt 'maas-node' maasng.create_subnet cidr, name, fabric, gateway_ip
1163 """
1164
1165 fabric_id = get_fabricid(fabric)
1166 result = {}
azvyagintsevf3515c82018-06-26 18:59:05 +03001167 vlan=str(vlan)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001168 data = {
1169 "cidr": cidr,
1170 "name": name,
1171 "fabric": str(fabric_id),
1172 "gateway_ip": gateway_ip,
azvyagintsevf3515c82018-06-26 18:59:05 +03001173 'vlan': vlan,
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001174 }
1175 maas = _create_maas_client()
azvyagintsevf3515c82018-06-26 18:59:05 +03001176 # FIXME: vlan definition not work in 2.3.3-6498-ge4db91d.
1177 LOG.warning("Ignoring parameter vlan:{}".format(vlan))
1178 data.pop('vlan','')
1179 try:
1180 if update:
1181 json_res = json.loads(maas.put(u"api/2.0/subnets/{0}/".format(subnet_id), **data).read())
1182 else:
1183 json_res = json.loads(maas.post(u"api/2.0/subnets/", None, **data).read())
1184 except Exception as inst:
1185 LOG.debug("create_subnet data:{}".format(data))
1186 try:
1187 m = inst.readlines()
1188 except:
1189 m = inst.message
1190 LOG.error("Message:{0}".format(m))
1191 result['result'] = False
1192 result['comment'] = 'Error creating subnet: {0}'.format(name)
1193 result['error'] = m
1194 return result
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001195 LOG.debug("create_subnet:{}".format(json_res))
azvyagintsevf3515c82018-06-26 18:59:05 +03001196 result["new"] = "Subnet {0} with CIDR {1}" \
1197 "and gateway {2} was created".format(name, cidr, gateway_ip)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001198
1199 return result
1200
1201
1202def get_subnet(subnet):
1203 """
1204 Get details for specific subnet
1205
1206 CLI Example:
1207
1208 .. code-block:: bash
1209
1210 salt 'maas-node' maasng.get_subnet subnet_name
1211 """
1212 try:
azvyagintsevf3515c82018-06-26 18:59:05 +03001213 return list_subnets()[subnet]
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001214 except KeyError:
1215 return {"error": "Subnet not found on MaaS server"}
1216
1217
1218def get_subnetid(subnet):
1219 """
1220 Get id for specific subnet
1221
1222 CLI Example:
1223
1224 .. code-block:: bash
1225
1226 salt 'maas-node' maasng.get_subnetid subnet_name
1227 """
1228 try:
azvyagintsevf3515c82018-06-26 18:59:05 +03001229 return list_subnets()[subnet]['id']
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001230 except KeyError:
1231 return {"error": "Subnet not found on MaaS server"}
1232
1233
1234def list_ipranges():
1235 """
1236 Get list of all ipranges from maas server
1237
1238 CLI Example:
1239
1240 .. code-block:: bash
1241
1242 salt 'maas-node' maasng.list_ipranges
1243 """
1244 ipranges = {}
1245 maas = _create_maas_client()
1246 json_res = json.loads(maas.get(u'api/2.0/ipranges/').read())
1247 for item in json_res:
1248 ipranges[item["start_ip"]] = item
1249 return ipranges
1250
1251
azvyagintsevf0904ac2018-07-05 18:53:26 +03001252def create_iprange(type_range, start_ip, end_ip, subnet=None, comment=None):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001253 """
1254 Create ip range
1255
1256 CLI Example:
1257
1258 .. code-block:: bash
1259
1260 salt 'maas-node' maasng.create_iprange type, start ip, end ip, comment
1261 """
1262 result = {}
1263
1264 data = {
1265 "type": type_range,
1266 "start_ip": start_ip,
1267 "end_ip": end_ip,
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001268 }
azvyagintsevf3515c82018-06-26 18:59:05 +03001269 if comment:
1270 data['comment'] = comment
azvyagintsevf0904ac2018-07-05 18:53:26 +03001271 if subnet:
1272 subnet_id = list_subnets()[subnet]['id']
1273 data['subnet'] = str(subnet_id)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001274 maas = _create_maas_client()
azvyagintsevf3515c82018-06-26 18:59:05 +03001275 _name = "Type:{}: {}-{}".format(type_range, start_ip, end_ip)
1276 try:
1277 json_res = json.loads(
1278 maas.post(u"api/2.0/ipranges/", None, **data).read())
1279 except Exception as inst:
1280 try:
1281 m = inst.readlines()
1282 except:
1283 m = inst.message
1284 LOG.error("Message:{0}".format(m))
1285 result['result'] = False
1286 result['comment'] = 'Error creating iprange:{0}'.format(_name)
1287 result['error'] = m
1288 return result
1289 result["new"] = "Iprange: {0} has been created".format(_name)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001290 LOG.debug("create_iprange:{}".format(json_res))
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001291
1292 return result
1293
1294
1295def get_iprangeid(start_ip):
1296 """
1297 Get id for ip range from maas server
1298
1299 CLI Example:
1300
1301 .. code-block:: bash
1302
1303 salt 'maas-node' maasng.get_iprangeid start_ip
1304 """
1305 try:
1306 return list_ipranges()[start_ip]['id']
1307 except KeyError:
1308 return {"error": "Ip range not found on MaaS server"}
1309
1310
1311def get_startip(start_ip):
1312 """
1313 Get start ip for ip range
1314
1315 CLI Example:
1316
1317 .. code-block:: bash
1318
1319 salt 'maas-node' maasng.get_startip start ip
1320 """
1321 try:
1322 return list_ipranges()[start_ip]
1323 except KeyError:
1324 return {"error": "Ip range not found on MaaS server"}
azvyagintsevbca1f462018-05-25 19:06:46 +03001325# END NETWORKING
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001326
1327# MAAS CONFIG SECTION
1328
1329
azvyagintsev58947072018-06-29 12:09:48 +03001330def _getHTTPCode(url):
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001331 code = '003'
1332 m = ''
azvyagintsev58947072018-06-29 12:09:48 +03001333 try:
1334 connection = urllib2.urlopen(url)
1335 code = connection.getcode()
1336 connection.close()
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001337 except (urllib2.HTTPError, urllib2.URLError) as e:
1338 try:
1339 code = e.getcode()
1340 except:
1341 m = e.reason
1342 pass
azvyagintsevf3515c82018-06-26 18:59:05 +03001343 LOG.debug("Unexpected http code:{} from "
1344 "url:{}\nwith message:{}".format(code, url, m))
azvyagintsev58947072018-06-29 12:09:48 +03001345 pass
1346 return code
1347
1348
1349def wait_for_http_code(url=None, expected=[200]):
1350 """
1351 Simple function, which just wait for avaible api, aka wait for 200.
1352
1353 CLI Example:
1354
1355 .. code-block:: bash
1356
1357 salt 'maas-node' maasng.wait_for_http_code url expected=[200]
1358
1359 """
1360 ret = {}
1361 started_at = time.time()
1362 poll_time = 5
1363 timeout = 60 * 2
1364 while _getHTTPCode(url) not in expected:
1365 c_timeout = timeout - (time.time() - started_at)
1366 if c_timeout <= 0:
1367 ret['result'] = False
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001368 ret["comment"] = "api:{} not answered in time".format(url)
azvyagintsev58947072018-06-29 12:09:48 +03001369 return ret
1370 LOG.info(
1371 "Waiting for api:{0}\n"
1372 "sleep for:{1}s "
1373 "Left:{2}/{3}s".format(url, poll_time, round(c_timeout),
1374 timeout))
1375 time.sleep(poll_time)
1376 ret['result'] = True
1377 ret["comment"] = "MAAS API:{} up.".format(url)
1378 return ret
1379
1380
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001381def _get_boot_source_id_by_url(url):
1382 # FIXME: fix ret\validation
1383 try:
1384 bs_id = get_boot_source(url=url)["id"]
1385 except KeyError:
1386 return {"error": "boot-source:{0} not exist!".format(url)}
1387 return bs_id
1388
1389
1390def get_boot_source(url=None):
1391 """
1392 Read a boot source by url. If url not specified - return all.
1393
1394 CLI Example:
1395
1396 .. code-block:: bash
1397
1398 salt 'maas-node' maasng.get_boot_source url
1399
1400 """
1401 boot_sources = {}
1402 maas = _create_maas_client()
1403 json_res = json.loads(maas.get(u'api/2.0/boot-sources/').read() or 'null')
1404 for item in json_res:
1405 boot_sources[str(item["url"])] = item
1406 if url:
1407 return boot_sources.get(url, {})
1408 return boot_sources
1409
1410
1411def delete_boot_source(url, bs_id=None):
1412 """
1413 Delete a boot source by url.
1414
1415 CLI Example:
1416
1417 .. code-block:: bash
1418
1419 sal 'maas-node' maasng.delete url
1420
1421 """
1422 result = {}
1423 if not bs_id:
1424 bs_id = _get_boot_source_id_by_url(url)
1425 maas = _create_maas_client()
1426 json_res = json.loads(maas.delete(
1427 u'/api/2.0/boot-sources/{0}/'.format(bs_id)).read() or 'null')
1428 LOG.debug("delete_boot_source:{}".format(json_res))
1429 result["new"] = "Boot-resource {0} deleted".format(url)
1430 return result
1431
1432
1433def boot_sources_delete_all_others(except_urls=[]):
1434 """
1435 Delete all boot-sources, except defined in 'except_urls' list.
1436 """
1437 result = {}
1438 maas_boot_sources = get_boot_source()
1439 if 0 in [len(except_urls), len(maas_boot_sources)]:
1440 result['result'] = None
1441 result[
1442 "comment"] = "Exclude or maas sources for delete empty. No changes goinng to be."
1443 return result
1444 for url in maas_boot_sources.keys():
1445 if url not in except_urls:
1446 LOG.info("Removing boot-source:{}".format(url))
1447 boot_resources_import(action='stop_import', wait=True)
1448 result["changes"] = delete_boot_source(url)
1449 return result
1450
1451
1452def create_boot_source(url, keyring_filename='', keyring_data='', wait=False):
1453 """
1454 Create and import maas boot-source: link to maas-ephemeral repo
1455 Be aware, those step will import resource to rack ctrl, but you also need to import
1456 them into the region!
1457
1458
1459 :param url: The URL of the BootSource.
1460 :param keyring_filename: The path to the keyring file for this BootSource.
1461 :param keyring_data: The GPG keyring for this BootSource, base64-encoded data.
1462
1463 """
1464
1465 # TODO: not work with 'update' currently => keyring update may fail.
1466 result = {}
1467
1468 data = {
1469 "url": url,
1470 "keyring_filename": keyring_filename,
1471 "keyring_data": str(keyring_data),
1472 }
1473
1474 maas = _create_maas_client()
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001475 if url in get_boot_source():
1476 result['result'] = None
1477 result["comment"] = "boot resource already exist"
1478 return result
1479
1480 # NOTE: maas.post will return 400, if url already defined.
1481 json_res = json.loads(
1482 maas.post(u'api/2.0/boot-sources/', None, **data).read())
1483 if wait:
1484 LOG.debug(
1485 "Sleep for 5s,to get MaaS some time to process previous request")
1486 time.sleep(5)
1487 ret = boot_resources_is_importing(wait=True)
1488 if ret is dict:
1489 return ret
1490 LOG.debug("create_boot_source:{}".format(json_res))
1491 result["new"] = "boot resource {0} was created".format(json_res["url"])
1492
1493 return result
1494
1495
1496def boot_resources_import(action='import', wait=False):
1497 """
1498 import/stop_import the boot resources.
1499
1500 :param action: import\stop_import
1501 :param wait: True\False. Wait till process finished.
1502
1503 CLI Example:
1504
1505 .. code-block:: bash
1506
1507 salt 'maas-node' maasng.boot_resources_import action='import'
1508
1509 """
1510 maas = _create_maas_client()
1511 # Have no idea why, but usual jsonloads not work here..
1512 imp = maas.post(u'api/2.0/boot-resources/', action)
1513 if imp.code == 200:
1514 LOG.debug('boot_resources_import:{}'.format(imp.readline()))
1515 if wait:
1516 boot_resources_is_importing(wait=True)
1517 return True
1518 else:
1519 return False
1520
1521
1522def boot_resources_is_importing(wait=False):
1523 maas = _create_maas_client()
1524 result = {}
1525 if wait:
1526 started_at = time.time()
1527 poll_time = 5
1528 timeout = 60 * 15
1529 while boot_resources_is_importing(wait=False):
1530 c_timeout = timeout - (time.time() - started_at)
1531 if c_timeout <= 0:
1532 result['result'] = False
1533 result["comment"] = "Boot-resources import not finished in time"
1534 return result
1535 LOG.info(
1536 "Waiting boot-resources import done\n"
1537 "sleep for:{}s "
1538 "Left:{}/{}s".format(poll_time, round(c_timeout), timeout))
1539 time.sleep(poll_time)
1540 return json.loads(
1541 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1542 else:
1543 return json.loads(
1544 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1545
1546#####
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001547# def boot_sources_selections_delete_all_others(except_urls=[]):
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001548# """
1549# """
1550# result = {}
1551# return result
1552
1553
1554def is_boot_source_selections_in(dict1, list1):
1555 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001556 Check that requested boot-selection already in maas bs selections,
1557 if True- return bss id.
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001558 # FIXME: those hack check doesn't look good.
1559 """
1560 for bs in list1:
1561 same = set(dict1.keys()) & set(bs.keys())
1562 if all(elem in same for elem in
1563 ['os', 'release', 'arches', 'subarches', 'labels']):
azvyagintsevf3515c82018-06-26 18:59:05 +03001564 LOG.debug("boot-selection in maas:{0}\n"
1565 "looks same to requested:{1}".format(bs, dict1))
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001566 return bs['id']
1567 return False
1568
1569
1570def get_boot_source_selections(bs_url):
1571 """
1572 Get boot-source selections.
1573 """
1574 # check for key_error!
1575 bs_id = _get_boot_source_id_by_url(bs_url)
1576 maas = _create_maas_client()
1577 json_res = json.loads(
1578 maas.get(u'/api/2.0/boot-sources/{0}/selections/'.format(bs_id)).read())
1579 LOG.debug(
1580 "get_boot_source_selections for url:{} \n{}".format(bs_url, json_res))
1581 return json_res
1582
1583
1584def create_boot_source_selections(bs_url, os, release, arches="*",
1585 subarches="*", labels="*", wait=True):
1586 """
1587 Create a new boot source selection for bs_url.
1588 :param os: The OS (e.g. ubuntu, centos) for which to import resources.Required.
1589 :param release: The release for which to import resources. Required.
1590 :param arches: The architecture list for which to import resources.
1591 :param subarches: The subarchitecture list for which to import resources.
1592 :param labels: The label lists for which to import resources.
1593 """
1594
azvyagintsevcb54d142018-06-19 16:18:32 +03001595 result = { "result" : True, 'name' : bs_url, 'changes' : None }
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001596
1597 data = {
1598 "os": os,
1599 "release": release,
1600 "arches": arches,
1601 "subarches": subarches,
1602 "labels": labels,
1603 }
1604
1605 maas = _create_maas_client()
1606 bs_id = _get_boot_source_id_by_url(bs_url)
1607 # TODO add pre-create verify
1608 maas_bs_s = get_boot_source_selections(bs_url)
1609 if is_boot_source_selections_in(data, maas_bs_s):
1610 result["result"] = True
azvyagintsevf3515c82018-06-26 18:59:05 +03001611 result["comment"] = 'Requested boot-source selection ' \
1612 'for {0} already exist.'.format(
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001613 bs_url)
1614 return result
1615
1616 # NOTE: maas.post will return 400, if url already defined.
azvyagintsevcb54d142018-06-19 16:18:32 +03001617 # Also, maas need's some time to import info about stream.
1618 # unfortunatly, maas don't have any call to check stream-import-info - so, we need to implement
1619 # at least simple retry ;(
1620 json_res = False
1621 poll_time = 5
1622 for i in range(0,5):
1623 try:
1624 json_res = json.loads(
1625 maas.post(u'api/2.0/boot-sources/{0}/selections/'.format(bs_id), None,
1626 **data).read())
1627 except Exception as inst:
1628 m = inst.readlines()
azvyagintsevf3515c82018-06-26 18:59:05 +03001629 LOG.warning("boot_source_selections "
1630 "catch error during processing. Most-probably, "
1631 "streams not imported yet.\nSleep:{}s"
1632 "Retry:{}/5".format(poll_time, i))
azvyagintsevcb54d142018-06-19 16:18:32 +03001633 LOG.warning("Message:{0}".format(m))
1634 time.sleep(poll_time)
1635 continue
1636 break
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001637 LOG.debug("create_boot_source_selections:{}".format(json_res))
azvyagintsevcb54d142018-06-19 16:18:32 +03001638 if not json_res:
1639 result["result"] = False
azvyagintsevf3515c82018-06-26 18:59:05 +03001640 result["comment"] = 'Failed to create requested boot-source selection' \
1641 ' for {0}.'.format(bs_url)
azvyagintsevcb54d142018-06-19 16:18:32 +03001642 return result
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001643 if wait:
1644 LOG.debug(
1645 "Sleep for 5s,to get MaaS some time to process previous request")
1646 time.sleep(5)
1647 ret = boot_resources_import(action='import', wait=True)
1648 if ret is dict:
1649 return ret
azvyagintsevcb54d142018-06-19 16:18:32 +03001650 result["comment"] = "boot-source selection for {0} was created".format(bs_url)
1651 result["new"] = data
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001652
1653 return result
1654
1655# END MAAS CONFIG SECTION
azvyagintsevcb54d142018-06-19 16:18:32 +03001656
1657# RACK CONTROLLERS SECTION
1658
1659
1660def get_rack(hostname):
1661 """
1662 Get information about specified rackd
1663
1664 CLI Example:
1665
1666 .. code-block:: bash
1667
1668 salt-call maasng.get_rack rack_hostname
1669 """
1670 try:
1671 return list_racks()[hostname]
1672 except KeyError:
1673 return {"error": "rack:{} not found on MaaS server".format(hostname)}
1674
1675
azvyagintsev6913e5e2018-07-05 11:42:53 +03001676def list_racks(sort_by='hostname'):
azvyagintsevcb54d142018-06-19 16:18:32 +03001677 """
1678 Get list of all rack controllers from maas server
1679
1680 CLI Example:
1681
1682 .. code-block:: bash
1683
1684 salt-call maasng.list_racks
1685 """
1686 racks = {}
1687 maas = _create_maas_client()
1688 json_res = json.loads(
1689 maas.get(u"/api/2.0/rackcontrollers/").read() or 'null')
1690 for item in json_res:
azvyagintsev6913e5e2018-07-05 11:42:53 +03001691 racks[item[sort_by]] = item
azvyagintsevcb54d142018-06-19 16:18:32 +03001692 return racks
1693
1694
1695def sync_bs_to_rack(hostname=None):
1696 """
1697 Sync RACK boot-sources with REGION. If no hostname probided - sync to all.
1698
1699 CLI Example:
1700
1701 .. code-block:: bash
1702
1703 salt-call maasng.sync_bs_to_rack rack_hostname
1704 """
1705 ret = {}
1706 maas = _create_maas_client()
1707 if not hostname:
1708 LOG.info("boot-sources sync initiated for ALL Rack's")
1709 # Convert to json-like format
1710 json_res = json.loads('["{0}"]'.format(
1711 maas.post(u"/api/2.0/rackcontrollers/",
1712 'import_boot_images').read()))
1713 LOG.debug("sync_bs_to_rack:{}".format(json_res))
1714 ret['result'] = True
1715 ret['comment'] = "boot-sources sync initiated for ALL Rack's"
1716 return ret
1717 LOG.info("boot-sources sync initiated for RACK:{0}".format(hostname))
1718 # Convert to json-like format
1719 json_res = json.loads('["{0}"]'.format(maas.post(
1720 u"/api/2.0/rackcontrollers/{0}/".format(
1721 get_rack(hostname)['system_id']),
1722 'import_boot_images').read()))
1723 LOG.debug("sync_bs_to_rack:{}".format(json_res))
1724 ret['result'] = True
1725 ret['comment'] = "boot-sources sync initiated for {0} Rack's".format(
1726 hostname)
1727 return
1728
1729
1730def rack_list_boot_imgs(hostname):
1731 ret = {}
1732 maas = _create_maas_client()
1733 LOG.debug("rack_list_boot_imgs:{}".format(hostname))
1734 ret = json.loads(maas.get(u"/api/2.0/rackcontrollers/{0}/".format(
1735 get_rack(hostname)['system_id']), 'list_boot_images').read() or 'null')
1736 return ret
1737
1738
1739def is_rack_synced(hostname):
1740 rez = rack_list_boot_imgs(hostname)['status']
1741 if rez == 'synced':
1742 return True
1743 return False
1744
1745# TODO do we actually need _exact_ check per-pack?
1746# def wait_for_images_on_rack(hostname):
1747#
1748# """
1749# WA function, to be able check that RACK actually done SYNC images
1750# for REQUIRED images at least.
1751# Required image to be fetched from
1752# reclass:maas:region:boot_sources_selections:[keys]:os/release formation
1753#
1754# CLI Example:
1755#
1756# .. code-block:: bash
1757#
1758# salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
1759# """
1760# try:
1761# bss = __salt__['config.get']('maas')['region']['boot_sources_selections']
1762# except KeyError:
1763# ret['result'] = None
1764# ret['comment'] = "boot_sources_selections definition for sync not found."
1765# return ret
1766# s_names = []
1767# # Format u'name': u'ubuntu/xenial'
1768# for v in bss.values():s_names.append("{0}/{1}".format(v['os'],v['release']))
1769# # Each names, should be in rack and whole rack should be in sync-ed state
1770
1771
1772def sync_and_wait_bs_to_all_racks():
1773 """
1774 Sync ALL rack's with regions source images.
1775
1776 CLI Example:
1777
1778 .. code-block:: bash
1779
1780 salt-call maasng.sync_and_wait_bs_to_all_racks
1781 """
1782 sync_bs_to_rack()
1783 for rack in list_racks().keys():
1784 wait_for_sync_bs_to_rack(hostname=rack)
1785 return True
1786
1787
1788def wait_for_sync_bs_to_rack(hostname=None):
1789 """
1790 Wait for boot images sync finished, on exact rack
1791
1792 CLI Example:
1793
1794 .. code-block:: bash
1795
1796 salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
1797 """
1798 ret = {}
1799 started_at = time.time()
1800 poll_time = 5
1801 timeout = 60 * 15
1802 while not is_rack_synced(hostname):
1803 c_timeout = timeout - (time.time() - started_at)
1804 if c_timeout <= 0:
1805 ret['result'] = False
1806 ret[
1807 "comment"] = "Boot-resources sync on rackd:{0}" \
1808 "not finished in time".format(
1809 hostname)
1810 return ret
1811 LOG.info(
1812 "Waiting boot-resources sync done to rack:{0}\n"
1813 "sleep for:{1}s "
1814 "Left:{2}/{3}s".format(hostname, poll_time, round(c_timeout),
1815 timeout))
1816 time.sleep(poll_time)
1817 ret['result'] = is_rack_synced(hostname)
1818 ret["comment"] = "Boot-resources sync on rackd:{0} finished".format(
1819 hostname)
1820 return ret
1821
1822# END RACK CONTROLLERS SECTION