blob: 1c829121fffdf9f8939960b1043047906a106c2b [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)
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001118 json_res = json.loads(maas.post(
1119 u'api/2.0/fabrics/{0}/vlans/'.format(fabric_id), None, **data).read())
azvyagintsevf3515c82018-06-26 18:59:05 +03001120 except Exception as inst:
1121 LOG.debug("create_vlan_in_fabric data:{}".format(data))
1122 try:
1123 m = inst.readlines()
1124 except:
1125 m = inst.message
1126 LOG.error("Message:{0}".format(m))
1127 result['result'] = False
1128 result['comment'] = 'Error updating vlan: {0}'.format(name)
1129 result['error'] = m
1130 return result
1131 LOG.debug("create_vlan_in_fabric:{}".format(json_res))
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +02001132 result["new"] = "Vlan {0} was updated".format(json_res["name"])
1133
1134 return result
azvyagintsevbca1f462018-05-25 19:06:46 +03001135
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001136
azvyagintsevf3515c82018-06-26 18:59:05 +03001137def check_subnet(cidr, name, fabric, gateway_ip):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001138 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001139 Check that subnet exactly defined
1140 Return format:
1141 update - require update
1142 correct - fully coincides # not implemented
1143 not_exist - need's to be created
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001144 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001145
1146 ret = 'not_exist'
1147 subnets = list_subnets(sort_by='cidr')
1148 if cidr in subnets.keys():
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001149 LOG.debug("Requested subnet cidr:{} already exist".format(cidr))
1150 ret = 'update'
azvyagintsevf3515c82018-06-26 18:59:05 +03001151 return ret
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001152
1153
azvyagintsevf3515c82018-06-26 18:59:05 +03001154def create_subnet(cidr='', name='', fabric='', gateway_ip='', vlan='',
1155 update=False, subnet_id=''):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001156 """
1157 Create subnet
1158
1159 CLI Example:
1160
1161 .. code-block:: bash
1162
1163 salt 'maas-node' maasng.create_subnet cidr, name, fabric, gateway_ip
1164 """
1165
1166 fabric_id = get_fabricid(fabric)
1167 result = {}
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001168 vlan = str(vlan)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001169 data = {
1170 "cidr": cidr,
1171 "name": name,
1172 "fabric": str(fabric_id),
1173 "gateway_ip": gateway_ip,
azvyagintsevf3515c82018-06-26 18:59:05 +03001174 'vlan': vlan,
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001175 }
1176 maas = _create_maas_client()
azvyagintsevf3515c82018-06-26 18:59:05 +03001177 # FIXME: vlan definition not work in 2.3.3-6498-ge4db91d.
1178 LOG.warning("Ignoring parameter vlan:{}".format(vlan))
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001179 data.pop('vlan', '')
azvyagintsevf3515c82018-06-26 18:59:05 +03001180 try:
1181 if update:
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001182 json_res = json.loads(
1183 maas.put(u"api/2.0/subnets/{0}/".format(subnet_id), **data).read())
azvyagintsevf3515c82018-06-26 18:59:05 +03001184 else:
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001185 json_res = json.loads(
1186 maas.post(u"api/2.0/subnets/", None, **data).read())
azvyagintsevf3515c82018-06-26 18:59:05 +03001187 except Exception as inst:
1188 LOG.debug("create_subnet data:{}".format(data))
1189 try:
1190 m = inst.readlines()
1191 except:
1192 m = inst.message
1193 LOG.error("Message:{0}".format(m))
1194 result['result'] = False
1195 result['comment'] = 'Error creating subnet: {0}'.format(name)
1196 result['error'] = m
1197 return result
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001198 LOG.debug("create_subnet:{}".format(json_res))
azvyagintsevf3515c82018-06-26 18:59:05 +03001199 result["new"] = "Subnet {0} with CIDR {1}" \
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001200 "and gateway {2} was created".format(
1201 name, cidr, gateway_ip)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001202
1203 return result
1204
1205
1206def get_subnet(subnet):
1207 """
1208 Get details for specific subnet
1209
1210 CLI Example:
1211
1212 .. code-block:: bash
1213
1214 salt 'maas-node' maasng.get_subnet subnet_name
1215 """
1216 try:
azvyagintsevf3515c82018-06-26 18:59:05 +03001217 return list_subnets()[subnet]
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001218 except KeyError:
1219 return {"error": "Subnet not found on MaaS server"}
1220
1221
1222def get_subnetid(subnet):
1223 """
1224 Get id for specific subnet
1225
1226 CLI Example:
1227
1228 .. code-block:: bash
1229
1230 salt 'maas-node' maasng.get_subnetid subnet_name
1231 """
1232 try:
azvyagintsevf3515c82018-06-26 18:59:05 +03001233 return list_subnets()[subnet]['id']
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001234 except KeyError:
1235 return {"error": "Subnet not found on MaaS server"}
1236
1237
1238def list_ipranges():
1239 """
1240 Get list of all ipranges from maas server
1241
1242 CLI Example:
1243
1244 .. code-block:: bash
1245
1246 salt 'maas-node' maasng.list_ipranges
1247 """
1248 ipranges = {}
1249 maas = _create_maas_client()
1250 json_res = json.loads(maas.get(u'api/2.0/ipranges/').read())
1251 for item in json_res:
1252 ipranges[item["start_ip"]] = item
1253 return ipranges
1254
1255
azvyagintsevf0904ac2018-07-05 18:53:26 +03001256def create_iprange(type_range, start_ip, end_ip, subnet=None, comment=None):
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001257 """
1258 Create ip range
1259
1260 CLI Example:
1261
1262 .. code-block:: bash
1263
1264 salt 'maas-node' maasng.create_iprange type, start ip, end ip, comment
1265 """
1266 result = {}
1267
1268 data = {
1269 "type": type_range,
1270 "start_ip": start_ip,
1271 "end_ip": end_ip,
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001272 }
azvyagintsevf3515c82018-06-26 18:59:05 +03001273 if comment:
1274 data['comment'] = comment
azvyagintsevf0904ac2018-07-05 18:53:26 +03001275 if subnet:
1276 subnet_id = list_subnets()[subnet]['id']
1277 data['subnet'] = str(subnet_id)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001278 maas = _create_maas_client()
azvyagintsevf3515c82018-06-26 18:59:05 +03001279 _name = "Type:{}: {}-{}".format(type_range, start_ip, end_ip)
1280 try:
1281 json_res = json.loads(
1282 maas.post(u"api/2.0/ipranges/", None, **data).read())
1283 except Exception as inst:
1284 try:
1285 m = inst.readlines()
1286 except:
1287 m = inst.message
1288 LOG.error("Message:{0}".format(m))
1289 result['result'] = False
1290 result['comment'] = 'Error creating iprange:{0}'.format(_name)
1291 result['error'] = m
1292 return result
1293 result["new"] = "Iprange: {0} has been created".format(_name)
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001294 LOG.debug("create_iprange:{}".format(json_res))
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001295
1296 return result
1297
1298
1299def get_iprangeid(start_ip):
1300 """
1301 Get id for ip range from maas server
1302
1303 CLI Example:
1304
1305 .. code-block:: bash
1306
1307 salt 'maas-node' maasng.get_iprangeid start_ip
1308 """
1309 try:
1310 return list_ipranges()[start_ip]['id']
1311 except KeyError:
1312 return {"error": "Ip range not found on MaaS server"}
1313
1314
1315def get_startip(start_ip):
1316 """
1317 Get start ip for ip range
1318
1319 CLI Example:
1320
1321 .. code-block:: bash
1322
1323 salt 'maas-node' maasng.get_startip start ip
1324 """
1325 try:
1326 return list_ipranges()[start_ip]
1327 except KeyError:
1328 return {"error": "Ip range not found on MaaS server"}
azvyagintsevbca1f462018-05-25 19:06:46 +03001329# END NETWORKING
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001330
1331# MAAS CONFIG SECTION
1332
1333
azvyagintsev58947072018-06-29 12:09:48 +03001334def _getHTTPCode(url):
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001335 code = '003'
1336 m = ''
azvyagintsev58947072018-06-29 12:09:48 +03001337 try:
1338 connection = urllib2.urlopen(url)
1339 code = connection.getcode()
1340 connection.close()
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001341 except (urllib2.HTTPError, urllib2.URLError) as e:
1342 try:
1343 code = e.getcode()
1344 except:
1345 m = e.reason
1346 pass
azvyagintsevf3515c82018-06-26 18:59:05 +03001347 LOG.debug("Unexpected http code:{} from "
1348 "url:{}\nwith message:{}".format(code, url, m))
azvyagintsev58947072018-06-29 12:09:48 +03001349 pass
1350 return code
1351
1352
1353def wait_for_http_code(url=None, expected=[200]):
1354 """
1355 Simple function, which just wait for avaible api, aka wait for 200.
1356
1357 CLI Example:
1358
1359 .. code-block:: bash
1360
1361 salt 'maas-node' maasng.wait_for_http_code url expected=[200]
1362
1363 """
1364 ret = {}
1365 started_at = time.time()
1366 poll_time = 5
1367 timeout = 60 * 2
1368 while _getHTTPCode(url) not in expected:
1369 c_timeout = timeout - (time.time() - started_at)
1370 if c_timeout <= 0:
1371 ret['result'] = False
azvyagintsevfc1fcff2018-06-29 16:44:54 +03001372 ret["comment"] = "api:{} not answered in time".format(url)
azvyagintsev58947072018-06-29 12:09:48 +03001373 return ret
1374 LOG.info(
1375 "Waiting for api:{0}\n"
1376 "sleep for:{1}s "
1377 "Left:{2}/{3}s".format(url, poll_time, round(c_timeout),
1378 timeout))
1379 time.sleep(poll_time)
1380 ret['result'] = True
1381 ret["comment"] = "MAAS API:{} up.".format(url)
1382 return ret
1383
1384
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001385def _get_boot_source_id_by_url(url):
1386 # FIXME: fix ret\validation
1387 try:
1388 bs_id = get_boot_source(url=url)["id"]
1389 except KeyError:
1390 return {"error": "boot-source:{0} not exist!".format(url)}
1391 return bs_id
1392
1393
1394def get_boot_source(url=None):
1395 """
1396 Read a boot source by url. If url not specified - return all.
1397
1398 CLI Example:
1399
1400 .. code-block:: bash
1401
1402 salt 'maas-node' maasng.get_boot_source url
1403
1404 """
1405 boot_sources = {}
1406 maas = _create_maas_client()
1407 json_res = json.loads(maas.get(u'api/2.0/boot-sources/').read() or 'null')
1408 for item in json_res:
1409 boot_sources[str(item["url"])] = item
1410 if url:
1411 return boot_sources.get(url, {})
1412 return boot_sources
1413
1414
1415def delete_boot_source(url, bs_id=None):
1416 """
1417 Delete a boot source by url.
1418
1419 CLI Example:
1420
1421 .. code-block:: bash
1422
1423 sal 'maas-node' maasng.delete url
1424
1425 """
1426 result = {}
1427 if not bs_id:
1428 bs_id = _get_boot_source_id_by_url(url)
1429 maas = _create_maas_client()
1430 json_res = json.loads(maas.delete(
1431 u'/api/2.0/boot-sources/{0}/'.format(bs_id)).read() or 'null')
1432 LOG.debug("delete_boot_source:{}".format(json_res))
1433 result["new"] = "Boot-resource {0} deleted".format(url)
1434 return result
1435
1436
1437def boot_sources_delete_all_others(except_urls=[]):
1438 """
1439 Delete all boot-sources, except defined in 'except_urls' list.
1440 """
azvyagintseve2e37a12018-11-01 14:45:49 +02001441 result = {'changes': {}}
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001442 maas_boot_sources = get_boot_source()
1443 if 0 in [len(except_urls), len(maas_boot_sources)]:
1444 result['result'] = None
azvyagintseve2e37a12018-11-01 14:45:49 +02001445 result["comment"] = "'except_urls' or 'maas boot-sources' for " \
1446 "delete empty. No changes goinng to be."
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001447 return result
azvyagintseve2e37a12018-11-01 14:45:49 +02001448 # FIXME: fix 'changes' accumulator
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001449 for url in maas_boot_sources.keys():
1450 if url not in except_urls:
1451 LOG.info("Removing boot-source:{}".format(url))
1452 boot_resources_import(action='stop_import', wait=True)
1453 result["changes"] = delete_boot_source(url)
1454 return result
1455
1456
1457def create_boot_source(url, keyring_filename='', keyring_data='', wait=False):
1458 """
1459 Create and import maas boot-source: link to maas-ephemeral repo
1460 Be aware, those step will import resource to rack ctrl, but you also need to import
1461 them into the region!
1462
1463
1464 :param url: The URL of the BootSource.
1465 :param keyring_filename: The path to the keyring file for this BootSource.
1466 :param keyring_data: The GPG keyring for this BootSource, base64-encoded data.
1467
1468 """
1469
1470 # TODO: not work with 'update' currently => keyring update may fail.
1471 result = {}
1472
1473 data = {
1474 "url": url,
1475 "keyring_filename": keyring_filename,
1476 "keyring_data": str(keyring_data),
1477 }
1478
1479 maas = _create_maas_client()
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001480 if url in get_boot_source():
1481 result['result'] = None
1482 result["comment"] = "boot resource already exist"
1483 return result
1484
1485 # NOTE: maas.post will return 400, if url already defined.
1486 json_res = json.loads(
1487 maas.post(u'api/2.0/boot-sources/', None, **data).read())
1488 if wait:
1489 LOG.debug(
1490 "Sleep for 5s,to get MaaS some time to process previous request")
1491 time.sleep(5)
1492 ret = boot_resources_is_importing(wait=True)
1493 if ret is dict:
1494 return ret
1495 LOG.debug("create_boot_source:{}".format(json_res))
1496 result["new"] = "boot resource {0} was created".format(json_res["url"])
1497
1498 return result
1499
1500
1501def boot_resources_import(action='import', wait=False):
1502 """
1503 import/stop_import the boot resources.
1504
1505 :param action: import\stop_import
1506 :param wait: True\False. Wait till process finished.
1507
1508 CLI Example:
1509
1510 .. code-block:: bash
1511
1512 salt 'maas-node' maasng.boot_resources_import action='import'
1513
1514 """
1515 maas = _create_maas_client()
1516 # Have no idea why, but usual jsonloads not work here..
1517 imp = maas.post(u'api/2.0/boot-resources/', action)
1518 if imp.code == 200:
1519 LOG.debug('boot_resources_import:{}'.format(imp.readline()))
1520 if wait:
1521 boot_resources_is_importing(wait=True)
1522 return True
1523 else:
1524 return False
1525
1526
1527def boot_resources_is_importing(wait=False):
1528 maas = _create_maas_client()
1529 result = {}
1530 if wait:
1531 started_at = time.time()
1532 poll_time = 5
1533 timeout = 60 * 15
1534 while boot_resources_is_importing(wait=False):
1535 c_timeout = timeout - (time.time() - started_at)
1536 if c_timeout <= 0:
1537 result['result'] = False
1538 result["comment"] = "Boot-resources import not finished in time"
1539 return result
1540 LOG.info(
1541 "Waiting boot-resources import done\n"
1542 "sleep for:{}s "
1543 "Left:{}/{}s".format(poll_time, round(c_timeout), timeout))
1544 time.sleep(poll_time)
1545 return json.loads(
1546 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1547 else:
1548 return json.loads(
1549 maas.get(u'api/2.0/boot-resources/', 'is_importing').read())
1550
1551#####
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +02001552# def boot_sources_selections_delete_all_others(except_urls=[]):
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001553# """
1554# """
1555# result = {}
1556# return result
1557
1558
1559def is_boot_source_selections_in(dict1, list1):
1560 """
azvyagintsevf3515c82018-06-26 18:59:05 +03001561 Check that requested boot-selection already in maas bs selections,
1562 if True- return bss id.
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001563 # FIXME: those hack check doesn't look good.
1564 """
1565 for bs in list1:
1566 same = set(dict1.keys()) & set(bs.keys())
1567 if all(elem in same for elem in
1568 ['os', 'release', 'arches', 'subarches', 'labels']):
azvyagintsevf3515c82018-06-26 18:59:05 +03001569 LOG.debug("boot-selection in maas:{0}\n"
1570 "looks same to requested:{1}".format(bs, dict1))
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001571 return bs['id']
1572 return False
1573
1574
1575def get_boot_source_selections(bs_url):
1576 """
1577 Get boot-source selections.
1578 """
1579 # check for key_error!
1580 bs_id = _get_boot_source_id_by_url(bs_url)
1581 maas = _create_maas_client()
1582 json_res = json.loads(
1583 maas.get(u'/api/2.0/boot-sources/{0}/selections/'.format(bs_id)).read())
1584 LOG.debug(
1585 "get_boot_source_selections for url:{} \n{}".format(bs_url, json_res))
1586 return json_res
1587
1588
1589def create_boot_source_selections(bs_url, os, release, arches="*",
1590 subarches="*", labels="*", wait=True):
1591 """
1592 Create a new boot source selection for bs_url.
1593 :param os: The OS (e.g. ubuntu, centos) for which to import resources.Required.
1594 :param release: The release for which to import resources. Required.
1595 :param arches: The architecture list for which to import resources.
1596 :param subarches: The subarchitecture list for which to import resources.
1597 :param labels: The label lists for which to import resources.
1598 """
1599
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001600 result = {"result": True, 'name': bs_url, 'changes': None}
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001601
1602 data = {
1603 "os": os,
1604 "release": release,
1605 "arches": arches,
1606 "subarches": subarches,
1607 "labels": labels,
1608 }
1609
1610 maas = _create_maas_client()
1611 bs_id = _get_boot_source_id_by_url(bs_url)
1612 # TODO add pre-create verify
1613 maas_bs_s = get_boot_source_selections(bs_url)
1614 if is_boot_source_selections_in(data, maas_bs_s):
1615 result["result"] = True
azvyagintsevf3515c82018-06-26 18:59:05 +03001616 result["comment"] = 'Requested boot-source selection ' \
1617 'for {0} already exist.'.format(
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001618 bs_url)
1619 return result
1620
1621 # NOTE: maas.post will return 400, if url already defined.
azvyagintsevcb54d142018-06-19 16:18:32 +03001622 # Also, maas need's some time to import info about stream.
1623 # unfortunatly, maas don't have any call to check stream-import-info - so, we need to implement
1624 # at least simple retry ;(
1625 json_res = False
1626 poll_time = 5
azvyagintseve2e37a12018-11-01 14:45:49 +02001627 for i in range(0, 10):
azvyagintsevcb54d142018-06-19 16:18:32 +03001628 try:
1629 json_res = json.loads(
1630 maas.post(u'api/2.0/boot-sources/{0}/selections/'.format(bs_id), None,
1631 **data).read())
1632 except Exception as inst:
1633 m = inst.readlines()
azvyagintsevf3515c82018-06-26 18:59:05 +03001634 LOG.warning("boot_source_selections "
1635 "catch error during processing. Most-probably, "
azvyagintseve2e37a12018-11-01 14:45:49 +02001636 "streams data not imported yet.\nSleep:{}s "
1637 "Retry:{}/10".format(poll_time, i))
azvyagintsevcb54d142018-06-19 16:18:32 +03001638 LOG.warning("Message:{0}".format(m))
1639 time.sleep(poll_time)
1640 continue
1641 break
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001642 LOG.debug("create_boot_source_selections:{}".format(json_res))
azvyagintsevcb54d142018-06-19 16:18:32 +03001643 if not json_res:
1644 result["result"] = False
azvyagintsevf3515c82018-06-26 18:59:05 +03001645 result["comment"] = 'Failed to create requested boot-source selection' \
1646 ' for {0}.'.format(bs_url)
azvyagintsevcb54d142018-06-19 16:18:32 +03001647 return result
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001648 if wait:
1649 LOG.debug(
1650 "Sleep for 5s,to get MaaS some time to process previous request")
1651 time.sleep(5)
1652 ret = boot_resources_import(action='import', wait=True)
1653 if ret is dict:
1654 return ret
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001655 result["comment"] = "boot-source selection for {0} was created".format(
1656 bs_url)
azvyagintsevcb54d142018-06-19 16:18:32 +03001657 result["new"] = data
azvyagintsev3ff2ef12018-06-01 21:30:45 +03001658
1659 return result
1660
1661# END MAAS CONFIG SECTION
azvyagintsevcb54d142018-06-19 16:18:32 +03001662
1663# RACK CONTROLLERS SECTION
1664
1665
1666def get_rack(hostname):
1667 """
1668 Get information about specified rackd
1669
1670 CLI Example:
1671
1672 .. code-block:: bash
1673
1674 salt-call maasng.get_rack rack_hostname
1675 """
1676 try:
1677 return list_racks()[hostname]
1678 except KeyError:
1679 return {"error": "rack:{} not found on MaaS server".format(hostname)}
1680
1681
azvyagintsev6913e5e2018-07-05 11:42:53 +03001682def list_racks(sort_by='hostname'):
azvyagintsevcb54d142018-06-19 16:18:32 +03001683 """
1684 Get list of all rack controllers from maas server
1685
1686 CLI Example:
1687
1688 .. code-block:: bash
1689
1690 salt-call maasng.list_racks
1691 """
1692 racks = {}
1693 maas = _create_maas_client()
1694 json_res = json.loads(
1695 maas.get(u"/api/2.0/rackcontrollers/").read() or 'null')
1696 for item in json_res:
azvyagintsev6913e5e2018-07-05 11:42:53 +03001697 racks[item[sort_by]] = item
azvyagintsevcb54d142018-06-19 16:18:32 +03001698 return racks
1699
1700
1701def sync_bs_to_rack(hostname=None):
1702 """
1703 Sync RACK boot-sources with REGION. If no hostname probided - sync to all.
1704
1705 CLI Example:
1706
1707 .. code-block:: bash
1708
1709 salt-call maasng.sync_bs_to_rack rack_hostname
1710 """
1711 ret = {}
1712 maas = _create_maas_client()
1713 if not hostname:
1714 LOG.info("boot-sources sync initiated for ALL Rack's")
1715 # Convert to json-like format
1716 json_res = json.loads('["{0}"]'.format(
1717 maas.post(u"/api/2.0/rackcontrollers/",
1718 'import_boot_images').read()))
1719 LOG.debug("sync_bs_to_rack:{}".format(json_res))
1720 ret['result'] = True
1721 ret['comment'] = "boot-sources sync initiated for ALL Rack's"
1722 return ret
1723 LOG.info("boot-sources sync initiated for RACK:{0}".format(hostname))
1724 # Convert to json-like format
1725 json_res = json.loads('["{0}"]'.format(maas.post(
1726 u"/api/2.0/rackcontrollers/{0}/".format(
1727 get_rack(hostname)['system_id']),
1728 'import_boot_images').read()))
1729 LOG.debug("sync_bs_to_rack:{}".format(json_res))
1730 ret['result'] = True
1731 ret['comment'] = "boot-sources sync initiated for {0} Rack's".format(
1732 hostname)
1733 return
1734
1735
1736def rack_list_boot_imgs(hostname):
1737 ret = {}
1738 maas = _create_maas_client()
1739 LOG.debug("rack_list_boot_imgs:{}".format(hostname))
1740 ret = json.loads(maas.get(u"/api/2.0/rackcontrollers/{0}/".format(
1741 get_rack(hostname)['system_id']), 'list_boot_images').read() or 'null')
1742 return ret
1743
1744
1745def is_rack_synced(hostname):
1746 rez = rack_list_boot_imgs(hostname)['status']
1747 if rez == 'synced':
1748 return True
1749 return False
1750
1751# TODO do we actually need _exact_ check per-pack?
1752# def wait_for_images_on_rack(hostname):
1753#
1754# """
1755# WA function, to be able check that RACK actually done SYNC images
1756# for REQUIRED images at least.
1757# Required image to be fetched from
1758# reclass:maas:region:boot_sources_selections:[keys]:os/release formation
1759#
1760# CLI Example:
1761#
1762# .. code-block:: bash
1763#
1764# salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
1765# """
1766# try:
1767# bss = __salt__['config.get']('maas')['region']['boot_sources_selections']
1768# except KeyError:
1769# ret['result'] = None
1770# ret['comment'] = "boot_sources_selections definition for sync not found."
1771# return ret
1772# s_names = []
1773# # Format u'name': u'ubuntu/xenial'
1774# for v in bss.values():s_names.append("{0}/{1}".format(v['os'],v['release']))
1775# # Each names, should be in rack and whole rack should be in sync-ed state
1776
1777
1778def sync_and_wait_bs_to_all_racks():
1779 """
1780 Sync ALL rack's with regions source images.
1781
1782 CLI Example:
1783
1784 .. code-block:: bash
1785
1786 salt-call maasng.sync_and_wait_bs_to_all_racks
1787 """
1788 sync_bs_to_rack()
1789 for rack in list_racks().keys():
1790 wait_for_sync_bs_to_rack(hostname=rack)
1791 return True
1792
1793
1794def wait_for_sync_bs_to_rack(hostname=None):
1795 """
1796 Wait for boot images sync finished, on exact rack
1797
1798 CLI Example:
1799
1800 .. code-block:: bash
1801
1802 salt-call maasng.wait_for_sync_bs_to_rack rack_hostname
1803 """
1804 ret = {}
1805 started_at = time.time()
1806 poll_time = 5
1807 timeout = 60 * 15
1808 while not is_rack_synced(hostname):
1809 c_timeout = timeout - (time.time() - started_at)
1810 if c_timeout <= 0:
1811 ret['result'] = False
1812 ret[
1813 "comment"] = "Boot-resources sync on rackd:{0}" \
1814 "not finished in time".format(
1815 hostname)
1816 return ret
1817 LOG.info(
1818 "Waiting boot-resources sync done to rack:{0}\n"
1819 "sleep for:{1}s "
1820 "Left:{2}/{3}s".format(hostname, poll_time, round(c_timeout),
1821 timeout))
1822 time.sleep(poll_time)
1823 ret['result'] = is_rack_synced(hostname)
1824 ret["comment"] = "Boot-resources sync on rackd:{0} finished".format(
1825 hostname)
1826 return ret
1827
1828# END RACK CONTROLLERS SECTION
Pavel Cizinsky8f9ba8e2018-09-10 14:31:49 +02001829# SSHKEYS
1830
1831
1832def list_sshkeys():
1833 """
1834 Get list of all sshkeys
1835
1836 CLI Example:
1837
1838 .. code-block:: bash
1839
1840 salt 'maas-node' maasng.list_sshkeys
1841 salt-call maasng.list_sshkeys
1842 """
1843 ssh = {}
1844 maas = _create_maas_client()
1845 json_res = json.loads(maas.get(u'api/2.0/account/prefs/sshkeys/').read())
1846 LOG.info(json_res)
1847 for item in json_res:
1848 ssh[item["key"]] = item
1849 return ssh
1850
1851
1852def add_sshkey(sshkey):
1853 """
1854 Add SSH key for user to MAAS.
1855
1856 CLI Example:
1857
1858 .. code-block:: bash
1859
1860 salt 'maas-node' maasng.add_sshkey sshkey
1861 salt-call maasng.add_sshkey sshkey
1862 """
1863 data = {
1864 "key": sshkey,
1865 }
1866 result = {}
1867 maas = _create_maas_client()
1868
1869 maas.post(u"/api/2.0/account/prefs/sshkeys/", None, **data).read()
1870 result["new"] = "SSH Key {0} was added.".format(sshkey)
1871
1872 return result
1873
1874
1875def get_sshkey(sshkey):
1876 """
1877 Get start ip for ip range
1878
1879 CLI Example:
1880
1881 .. code-block:: bash
1882
1883 salt 'maas-node' maasng.get_sshkey sshkey
1884 salt-call maasng.get_sshkey sshkey
1885 """
1886 try:
1887 return list_sshkeys()[sshkey]
1888 except KeyError:
1889 return {"error": "SSH key not found on MaaS server"}
1890# END SSHKEYS