blob: 66987d20065d39ae039a31714c1984ef18cee8b6 [file] [log] [blame]
Ondrej Smolab57a23b2018-01-24 11:18:24 +01001import logging
2from salt.exceptions import CommandExecutionError, SaltInvocationError
3
4LOG = logging.getLogger(__name__)
5
6SIZE = {
7 "M": 1000000,
8 "G": 1000000000,
9 "T": 1000000000000,
10}
11
12RAID = {
13 0: "raid-0",
14 1: "raid-1",
15 5: "raid-5",
16 10: "raid-10",
17}
18
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020019
Ondrej Smolab57a23b2018-01-24 11:18:24 +010020def __virtual__():
21 '''
22 Load MaaSng module
23 '''
24 return 'maasng'
25
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020026
azvyagintsev3ff2ef12018-06-01 21:30:45 +030027def disk_layout_present(hostname, layout_type, root_size=None, root_device=None,
28 volume_group=None, volume_name=None, volume_size=None,
29 disk={}, **kwargs):
Ondrej Smolab57a23b2018-01-24 11:18:24 +010030 '''
31 Ensure that the disk layout does exist
32
33 :param name: The name of the cloud that should not exist
34 '''
35 ret = {'name': hostname,
36 'changes': {},
37 'result': True,
38 'comment': 'Disk layout "{0}" updated'.format(hostname)}
39
40 machine = __salt__['maasng.get_machine'](hostname)
41 if "error" in machine:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020042 ret['comment'] = "State execution failed for machine {0}".format(
43 hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +010044 ret['result'] = False
45 ret['changes'] = machine
46 return ret
47
48 if machine["status_name"] != "Ready":
49 ret['comment'] = 'Machine {0} is not in Ready state.'.format(hostname)
50 return ret
51
52 if __opts__['test']:
53 ret['result'] = None
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020054 ret['comment'] = 'Disk layout will be updated on {0}, this action will delete current layout.'.format(
55 hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +010056 return ret
57
58 if layout_type == "flat":
59
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020060 ret["changes"] = __salt__['maasng.update_disk_layout'](
61 hostname, layout_type, root_size, root_device)
Ondrej Smolab57a23b2018-01-24 11:18:24 +010062
63 elif layout_type == "lvm":
64
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020065 ret["changes"] = __salt__['maasng.update_disk_layout'](
66 hostname, layout_type, root_size, root_device, volume_group, volume_name, volume_size)
Ondrej Smolab57a23b2018-01-24 11:18:24 +010067
azvyagintsevbca1f462018-05-25 19:06:46 +030068 elif layout_type == "custom":
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +020069 ret["changes"] = __salt__[
70 'maasng.update_disk_layout'](hostname, layout_type)
azvyagintsevbca1f462018-05-25 19:06:46 +030071
Ondrej Smolab57a23b2018-01-24 11:18:24 +010072 else:
73 ret["comment"] = "Not supported layout provided. Choose flat or lvm"
74 ret['result'] = False
75
76 return ret
77
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020078
azvyagintsev3ff2ef12018-06-01 21:30:45 +030079def raid_present(hostname, name, level, devices=[], partitions=[],
80 partition_schema={}):
Ondrej Smolab57a23b2018-01-24 11:18:24 +010081 '''
82 Ensure that the raid does exist
83
84 :param name: The name of the cloud that should not exist
85 '''
86
87 ret = {'name': name,
88 'changes': {},
89 'result': True,
90 'comment': 'Raid {0} presented on {1}'.format(name, hostname)}
91
92 machine = __salt__['maasng.get_machine'](hostname)
93 if "error" in machine:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +020094 ret['comment'] = "State execution failed for machine {0}".format(
95 hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +010096 ret['result'] = False
97 ret['changes'] = machine
98 return ret
99
100 if machine["status_name"] != "Ready":
101 ret['comment'] = 'Machine {0} is not in Ready state.'.format(hostname)
102 return ret
103
104 if __opts__['test']:
105 ret['result'] = None
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200106 ret['comment'] = 'Raid {0} will be updated on {1}'.format(
107 name, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100108 return ret
109
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200110 # Validate that raid exists
111 # With correct devices/partition
112 # OR
113 # Create raid
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100114
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200115 ret["changes"] = __salt__['maasng.create_raid'](
116 hostname=hostname, name=name, level=level, disks=devices, partitions=partitions)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100117
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200118 # TODO partitions
119 ret["changes"].update(disk_partition_present(
120 hostname, name, partition_schema)["changes"])
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100121
122 if "error" in ret["changes"]:
123 ret["result"] = False
124
125 return ret
126
127
128def disk_partition_present(hostname, disk, partition_schema={}):
129 '''
130 Ensure that the disk has correct partititioning schema
131
132 :param name: The name of the cloud that should not exist
133 '''
134
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200135 # 1. Validate that disk has correct values for size and mount
136 # a. validate count of partitions
137 # b. validate size of partitions
138 # 2. If not delete all partitions on disk and recreate schema
139 # 3. Validate type exists
140 # if should not exits
141 # delete mount and unformat
142 # 4. Validate mount exists
143 # 5. if not enforce umount or mount
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100144
145 ret = {'name': hostname,
146 'changes': {},
147 'result': True,
148 'comment': 'Disk layout {0} presented'.format(disk)}
149
150 machine = __salt__['maasng.get_machine'](hostname)
151 if "error" in machine:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200152 ret['comment'] = "State execution failed for machine {0}".format(
153 hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100154 ret['result'] = False
155 ret['changes'] = machine
156 return ret
157
158 if machine["status_name"] != "Ready":
159 ret['comment'] = 'Machine {0} is not in Ready state.'.format(hostname)
160 return ret
161
162 if __opts__['test']:
163 ret['result'] = None
164 ret['comment'] = 'Partition schema will be changed on {0}'.format(disk)
165 return ret
166
167 partitions = __salt__['maasng.list_partitions'](hostname, disk)
168
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200169 # Calculate actual size in bytes from provided data
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100170 for part_name, part in partition_schema.iteritems():
171 size, unit = part["size"][:-1], part["size"][-1]
172 part["calc_size"] = int(size) * SIZE[unit]
173
174 if len(partitions) == len(partition_schema):
175
176 for part_name, part in partition_schema.iteritems():
177 LOG.info('validated {0}'.format(part["calc_size"]))
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200178 LOG.info('validated {0}'.format(
179 int(partitions[disk+"-"+part_name.split("-")[-1]]["size"])))
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100180 if part["calc_size"] == int(partitions[disk+"-"+part_name.split("-")[-1]]["size"]):
181 LOG.info('validated')
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200182 # TODO validate size (size from maas is not same as calculate?)
183 # TODO validate mount
184 # TODO validate fs type
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100185 else:
186 LOG.info('breaking')
187 break
188 return ret
189
190 #DELETE and RECREATE
191 LOG.info('delete')
192 for partition_name, partition in partitions.iteritems():
193 LOG.info(partition)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200194 # TODO IF LVM create ERROR
195 ret["changes"] = __salt__['maasng.delete_partition_by_id'](
196 hostname, disk, partition["id"])
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100197
198 LOG.info('recreating')
199 for part_name, part in partition_schema.iteritems():
200 LOG.info("partitition for creation")
201 LOG.info(part)
202 if "mount" not in part:
203 part["mount"] = None
204 if "type" not in part:
205 part["type"] = None
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200206 ret["changes"] = __salt__['maasng.create_partition'](
207 hostname, disk, part["size"], part["type"], part["mount"])
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100208
209 if "error" in ret["changes"]:
210 ret["result"] = False
211
212 return ret
213
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200214
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100215def volume_group_present(hostname, name, devices=[], partitions=[]):
216 '''
217 Ensure that the disk layout does exist
218
219 :param name: The name of the cloud that should not exist
220 '''
221 ret = {'name': hostname,
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200222 'changes': {},
223 'result': True,
224 'comment': 'LVM group {0} presented on {1}'.format(name, hostname)}
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100225
226 machine = __salt__['maasng.get_machine'](hostname)
227 if "error" in machine:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200228 ret['comment'] = "State execution failed for machine {0}".format(
229 hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100230 ret['result'] = False
231 ret['changes'] = machine
232 return ret
233
234 if machine["status_name"] != "Ready":
235 ret['comment'] = 'Machine {0} is not in Ready state.'.format(hostname)
236 return ret
237
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200238 # TODO validation if exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100239 vgs = __salt__['maasng.list_volume_groups'](hostname)
240
241 if name in vgs:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200242 # TODO validation for devices and partitions
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100243 return ret
244
245 if __opts__['test']:
246 ret['result'] = None
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200247 ret['comment'] = 'LVM group {0} will be updated on {1}'.format(
248 name, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100249 return ret
250
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200251 ret["changes"] = __salt__['maasng.create_volume_group'](
252 hostname, name, devices, partitions)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100253
254 if "error" in ret["changes"]:
255 ret["result"] = False
256
257 return ret
258
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200259
Ondrej Smola47b56752018-03-06 15:38:27 +0100260def volume_present(hostname, name, volume_group_name, size, type=None, mount=None):
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100261 '''
262 Ensure that the disk layout does exist
263
264 :param name: The name of the cloud that should not exist
265 '''
266
267 ret = {'name': hostname,
268 'changes': {},
269 'result': True,
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200270 'comment': 'LVM group {0} presented on {1}'.format(name, hostname)}
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100271
272 machine = __salt__['maasng.get_machine'](hostname)
273 if "error" in machine:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200274 ret['comment'] = "State execution failed for machine {0}".format(
275 hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100276 ret['result'] = False
277 ret['changes'] = machine
278 return ret
279
280 if machine["status_name"] != "Ready":
281 ret['comment'] = 'Machine {0} is not in Ready state.'.format(hostname)
282 return ret
283
284 if __opts__['test']:
285 ret['result'] = None
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200286 ret['comment'] = 'LVM volume {0} will be updated on {1}'.format(
287 name, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100288
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200289 # TODO validation if exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100290
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200291 ret["changes"] = __salt__['maasng.create_volume'](
292 hostname, name, volume_group_name, size, type, mount)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100293
294 return ret
295
296
297def select_boot_disk(hostname, name):
298 '''
299 Select disk that will be used to boot partition
300
301 :param name: The name of disk on machine
302 :param hostname: The hostname of machine
303 '''
304
305 ret = {'name': hostname,
306 'changes': {},
307 'result': True,
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200308 'comment': 'LVM group {0} presented on {1}'.format(name, hostname)}
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100309
310 machine = __salt__['maasng.get_machine'](hostname)
311 if "error" in machine:
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200312 ret['comment'] = "State execution failed for machine {0}".format(
313 hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100314 ret['result'] = False
315 ret['changes'] = machine
316 return ret
317
318 if machine["status_name"] != "Ready":
319 ret['comment'] = 'Machine {0} is not in Ready state.'.format(hostname)
320 return ret
321
322 if __opts__['test']:
323 ret['result'] = None
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200324 ret['comment'] = 'LVM volume {0} will be updated on {1}'.format(
325 name, hostname)
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100326
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200327 # TODO disk validation if exists
Ondrej Smolab57a23b2018-01-24 11:18:24 +0100328
329 ret["changes"] = __salt__['maasng.set_boot_disk'](hostname, name)
330
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200331 return ret
332
333
Pavel Cizinsky864a3292018-05-25 16:24:48 +0200334def update_vlan(name, fabric, vid, description, primary_rack, dhcp_on=False):
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200335 '''
336
337 :param name: Name of vlan
338 :param fabric: Name of fabric
339 :param vid: Vlan id
340 :param description: Description of vlan
341 :param dhcp_on: State of dhcp
Pavel Cizinsky864a3292018-05-25 16:24:48 +0200342 :param primary_rack: primary_rack
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200343
344 '''
345
346 ret = {'name': fabric,
347 'changes': {},
348 'result': True,
349 'comment': 'Module function maasng.update_vlan executed'}
350
351 ret["changes"] = __salt__['maasng.update_vlan'](
azvyagintsev3ff2ef12018-06-01 21:30:45 +0300352 name=name, fabric=fabric, vid=vid, description=description,
353 primary_rack=primary_rack, dhcp_on=dhcp_on)
Pavel Cizinsky0995e8f2018-05-04 17:10:37 +0200354
355 if "error" in fabric:
356 ret['comment'] = "State execution failed for fabric {0}".format(fabric)
357 ret['result'] = False
358 ret['changes'] = fabric
359 return ret
360
361 if __opts__['test']:
362 ret['result'] = None
363 ret['comment'] = 'Vlan {0} will be updated for {1}'.format(vid, fabric)
364 return ret
365
366 return ret
azvyagintsev3ff2ef12018-06-01 21:30:45 +0300367
368
369def boot_source_present(url, keyring_file='', keyring_data=''):
370 """
371 Process maas boot-sources: link to maas-ephemeral repo
372
373
374 :param url: The URL of the BootSource.
375 :param keyring_file: The path to the keyring file for this BootSource.
376 :param keyring_data: The GPG keyring for this BootSource, base64-encoded data.
377 """
378 ret = {'name': url,
379 'changes': {},
380 'result': True,
381 'comment': 'boot-source {0} presented'.format(url)}
382
383 if __opts__['test']:
384 ret['result'] = None
385 ret['comment'] = 'boot-source {0} will be updated'.format(url)
386
387 maas_boot_sources = __salt__['maasng.get_boot_source']()
388 # TODO imlpement check and update for keyrings!
389 if url in maas_boot_sources.keys():
390 ret["result"] = True
391 ret["comment"] = 'boot-source {0} alredy exist'.format(url)
392 return ret
393 ret["changes"] = __salt__['maasng.create_boot_source'](url,
394 keyring_filename=keyring_file,
395 keyring_data=keyring_data)
396 return ret
397
398
399def boot_sources_selections_present(bs_url, os, release, arches="*",
400 subarches="*", labels="*", wait=True):
401 """
402 Process maas boot-sources selection: set of resource configurathions, to be downloaded from boot-source bs_url.
403
404 :param bs_url: Boot-source url
405 :param os: The OS (e.g. ubuntu, centos) for which to import resources.Required.
406 :param release: The release for which to import resources. Required.
407 :param arches: The architecture list for which to import resources.
408 :param subarches: The subarchitecture list for which to import resources.
409 :param labels: The label lists for which to import resources.
410 :param wait: Initiate import and wait for done.
411
412 """
413 ret = {'name': bs_url,
414 'changes': {},
415 'result': True,
416 'comment': 'boot-source {0} selection present'.format(bs_url)}
417
418 if __opts__['test']:
419 ret['result'] = None
420 ret['comment'] = 'boot-source {0} selection will be updated'.format(
421 bs_url)
422
423 maas_boot_sources = __salt__['maasng.get_boot_source']()
424 if bs_url not in maas_boot_sources.keys():
425 ret["result"] = False
426 ret[
427 "comment"] = 'Requested boot-source {0} not exist! Unable to proceed selection for it'.format(
428 bs_url)
429 return ret
430
431 ret["changes"] = __salt__['maasng.create_boot_source_selections'](bs_url,
432 os,
433 release,
434 arches=arches,
435 subarches=subarches,
436 labels=labels,
437 wait=wait)
438 return ret
Pavel Cizinsky8dd85b52018-06-18 21:40:13 +0200439
440
441def iprange_present(name, type_range, start_ip, end_ip, comment):
442 '''
443
444 :param name: Name of iprange
445 :param type_range: Type of iprange
446 :param start_ip: Start ip of iprange
447 :param end_ip: End ip of iprange
448 :param comment: Comment for specific iprange
449
450 '''
451
452 ret = {'name': name,
453 'changes': {},
454 'result': True,
455 'comment': 'Module function maasng.iprange_present executed'}
456
457 start = __salt__['maasng.get_startip'](start_ip)
458 if 'start_ip' in start.keys():
459 if start["start_ip"] == start_ip:
460 ret['comment'] = 'Iprange {0} already exist.'.format(name)
461 return ret
462
463 if __opts__['test']:
464 ret['result'] = None
465 ret['comment'] = 'Ip range {0} will be created with start ip: {1} and end ip: {2} and type {3}'.format(
466 name, start_ip, end_ip, type_range)
467 return ret
468
469 ret["changes"] = __salt__['maasng.create_iprange'](
470 type_range=type_range, start_ip=start_ip, end_ip=end_ip, comment=comment)
471
472 return ret
473
474
475def subnet_present(cidr, name, fabric, gateway_ip):
476 '''
477
478 :param cidr: Cidr for subnet
479 :param name: Name of subnet
480 :param fabric: Name of fabric for subnet
481 :param gateway_ip: gateway_ip
482
483 '''
484
485 ret = {'name': name,
486 'changes': {},
487 'result': True,
488 'comment': 'Module function maasng.subnet_present executed'}
489
490 subnet = __salt__['maasng.get_subnet'](name)
491 if 'name' in subnet.keys():
492 if subnet['name'] == name:
493 ret['comment'] = 'Subnet {0} already exist for fabric {1}'.format(
494 name, fabric)
495 return ret
496
497 if __opts__['test']:
498 ret['result'] = None
499 ret['comment'] = 'Subnet {0} will be created for {1}'.format(
500 name, fabric)
501 return ret
502
503 ret["changes"] = __salt__['maasng.create_subnet'](
504 cidr=cidr, name=name, fabric=fabric, gateway_ip=gateway_ip)
505
506 return ret