blob: 791e185672431f6594e5c3335700ad09fea59c4c [file] [log] [blame]
Jiri Broulika2c79292017-02-05 21:01:38 +01001# -*- coding: utf-8 -*-
Adam Tenglere8afccc2017-06-27 17:57:21 +00002from __future__ import absolute_import, with_statement
Jiri Broulika2c79292017-02-05 21:01:38 +01003from pprint import pprint
4
5# Import python libs
6import logging
7
Jiri Broulika2c79292017-02-05 21:01:38 +01008# Get logging started
9log = logging.getLogger(__name__)
10
11# Function alias to not shadow built-ins
12__func_alias__ = {
13 'list_': 'list'
14}
15
16# Define the module's virtual name
17__virtualname__ = 'novang'
18
19
20def __virtual__():
21 '''
22 Only load this module if nova
23 is installed on this minion.
24 '''
Adam Tenglere8afccc2017-06-27 17:57:21 +000025 if check_nova():
Jiri Broulika2c79292017-02-05 21:01:38 +010026 return __virtualname__
27 return (False, 'The nova execution module failed to load: '
28 'only available if nova is installed.')
29
30
31__opts__ = {}
32
33
Adam Tenglere8afccc2017-06-27 17:57:21 +000034def _authng(profile=None, tenant_name=None):
Jiri Broulika2c79292017-02-05 21:01:38 +010035 '''
36 Set up nova credentials
37 '''
38 if profile:
39 credentials = __salt__['config.option'](profile)
40 user = credentials['keystone.user']
41 password = credentials['keystone.password']
Richard Felkl55d1f572017-02-15 16:41:53 +010042 if tenant_name:
43 tenant = tenant_name
44 else:
45 tenant = credentials['keystone.tenant']
Jiri Broulika2c79292017-02-05 21:01:38 +010046 auth_url = credentials['keystone.auth_url']
47 region_name = credentials.get('keystone.region_name', None)
48 api_key = credentials.get('keystone.api_key', None)
49 os_auth_system = credentials.get('keystone.os_auth_system', None)
Adam Tenglere8afccc2017-06-27 17:57:21 +000050 use_keystoneauth = credentials.get('keystone.use_keystoneauth', False)
51 verify = credentials.get('keystone.verify', True)
Jiri Broulika2c79292017-02-05 21:01:38 +010052 else:
53 user = __salt__['config.option']('keystone.user')
54 password = __salt__['config.option']('keystone.password')
55 tenant = __salt__['config.option']('keystone.tenant')
56 auth_url = __salt__['config.option']('keystone.auth_url')
57 region_name = __salt__['config.option']('keystone.region_name')
58 api_key = __salt__['config.option']('keystone.api_key')
59 os_auth_system = __salt__['config.option']('keystone.os_auth_system')
Adam Tenglere8afccc2017-06-27 17:57:21 +000060 use_keystoneauth = __salt__['config.option']('keystone.use_keystoneauth', False)
61 verify = __salt__['config.option']('keystone.verify', True)
Oleg Iurchenko6ba00f42018-02-21 13:45:49 +020062
Jiri Broulika2c79292017-02-05 21:01:38 +010063 kwargs = {
64 'username': user,
65 'password': password,
66 'api_key': api_key,
67 'project_id': tenant,
68 'auth_url': auth_url,
69 'region_name': region_name,
Adam Tenglere8afccc2017-06-27 17:57:21 +000070 'os_auth_plugin': os_auth_system,
71 'use_keystoneauth': use_keystoneauth,
Oleg Iurchenko6ba00f42018-02-21 13:45:49 +020072 'verify': verify,
73 'profile': profile
Jiri Broulika2c79292017-02-05 21:01:38 +010074 }
Adam Tenglere8afccc2017-06-27 17:57:21 +000075 return SaltNova(**kwargs)
Jiri Broulika2c79292017-02-05 21:01:38 +010076
77
Jiri Broulik70d9e3f2017-02-15 18:37:13 +010078def server_list(profile=None, tenant_name=None):
79 '''
80 Return list of active servers
81 CLI Example:
82 .. code-block:: bash
83 salt '*' nova.server_list
84 '''
Adam Tenglere8afccc2017-06-27 17:57:21 +000085 conn = _authng(profile, tenant_name)
Jiri Broulik70d9e3f2017-02-15 18:37:13 +010086 return conn.server_list()
87
88
89def server_get(name, tenant_name=None, profile=None):
90 '''
91 Return information about a server
92 '''
93 items = server_list(profile, tenant_name)
94 instance_id = None
95 for key, value in items.iteritems():
96 if key == name:
97 instance_id = value['id']
98 return instance_id
99
100
Jiri Broulika2c79292017-02-05 21:01:38 +0100101def get_connection_args(profile=None):
102 '''
103 Set up profile credentials
104 '''
105 if profile:
106 credentials = __salt__['config.option'](profile)
107 user = credentials['keystone.user']
108 password = credentials['keystone.password']
109 tenant = credentials['keystone.tenant']
110 auth_url = credentials['keystone.auth_url']
111
112 kwargs = {
113 'username': user,
114 'password': password,
115 'tenant': tenant,
116 'auth_url': auth_url
117 }
118 return kwargs
119
120
121def quota_list(tenant_name, profile=None):
122 '''
123 list quotas of a tenant
124 '''
125 connection_args = get_connection_args(profile)
126 tenant = __salt__['keystone.tenant_get'](name=tenant_name, profile=profile, **connection_args)
127 tenant_id = tenant[tenant_name]['id']
Adam Tenglere8afccc2017-06-27 17:57:21 +0000128 conn = _authng(profile)
Jiri Broulika2c79292017-02-05 21:01:38 +0100129 nt_ks = conn.compute_conn
130 item = nt_ks.quotas.get(tenant_id).__dict__
131 return item
132
133
134def quota_get(name, tenant_name, profile=None, quota_value=None):
135 '''
136 get specific quota value of a tenant
137 '''
138 item = quota_list(tenant_name, profile)
139 quota_value = item[name]
140 return quota_value
141
142
143def quota_update(tenant_name, profile=None, **quota_argument):
144 '''
145 update quota of specified tenant
146 '''
147 connection_args = get_connection_args(profile)
148 tenant = __salt__['keystone.tenant_get'](name=tenant_name, profile=profile, **connection_args)
149 tenant_id = tenant[tenant_name]['id']
Adam Tenglere8afccc2017-06-27 17:57:21 +0000150 conn = _authng(profile)
Jiri Broulika2c79292017-02-05 21:01:38 +0100151 nt_ks = conn.compute_conn
152 item = nt_ks.quotas.update(tenant_id, **quota_argument)
153 return item
Jiri Broulik70d9e3f2017-02-15 18:37:13 +0100154
155
Richard Felkl55d1f572017-02-15 16:41:53 +0100156def server_list(profile=None, tenant_name=None):
157 '''
158 Return list of active servers
159 CLI Example:
160 .. code-block:: bash
161 salt '*' nova.server_list
162 '''
Adam Tenglere8afccc2017-06-27 17:57:21 +0000163 conn = _authng(profile, tenant_name)
Richard Felkl55d1f572017-02-15 16:41:53 +0100164 return conn.server_list()
Jiri Broulika2c79292017-02-05 21:01:38 +0100165
Jiri Broulik70d9e3f2017-02-15 18:37:13 +0100166
Richard Felkl55d1f572017-02-15 16:41:53 +0100167def secgroup_list(profile=None, tenant_name=None):
168 '''
169 Return a list of available security groups (nova items-list)
170 CLI Example:
171 .. code-block:: bash
172 salt '*' nova.secgroup_list
173 '''
Adam Tenglere8afccc2017-06-27 17:57:21 +0000174 conn = _authng(profile, tenant_name)
Richard Felkl55d1f572017-02-15 16:41:53 +0100175 return conn.secgroup_list()
Jiri Broulika2c79292017-02-05 21:01:38 +0100176
Jiri Broulik70d9e3f2017-02-15 18:37:13 +0100177
Richard Felkl55d1f572017-02-15 16:41:53 +0100178def boot(name, flavor_id=0, image_id=0, profile=None, tenant_name=None, timeout=300, **kwargs):
179 '''
180 Boot (create) a new instance
181 name
182 Name of the new instance (must be first)
183 flavor_id
184 Unique integer ID for the flavor
185 image_id
186 Unique integer ID for the image
187 timeout
188 How long to wait, after creating the instance, for the provider to
189 return information about it (default 300 seconds).
190 .. versionadded:: 2014.1.0
191 CLI Example:
192 .. code-block:: bash
193 salt '*' nova.boot myinstance flavor_id=4596 image_id=2
194 The flavor_id and image_id are obtained from nova.flavor_list and
195 nova.image_list
196 .. code-block:: bash
197 salt '*' nova.flavor_list
198 salt '*' nova.image_list
199 '''
200 #kwargs = {'nics': nics}
Adam Tenglere8afccc2017-06-27 17:57:21 +0000201 conn = _authng(profile, tenant_name)
Richard Felkl55d1f572017-02-15 16:41:53 +0100202 return conn.boot(name, flavor_id, image_id, timeout, **kwargs)
Jiri Broulik70d9e3f2017-02-15 18:37:13 +0100203
204
Richard Felkl55d1f572017-02-15 16:41:53 +0100205def network_show(name, profile=None):
Adam Tenglere8afccc2017-06-27 17:57:21 +0000206 conn = _authng(profile)
Richard Felkl55d1f572017-02-15 16:41:53 +0100207 return conn.network_show(name)
Jiri Broulik70d9e3f2017-02-15 18:37:13 +0100208
209
210def availability_zone_list(profile=None):
211 '''
212 list existing availability zones
213 '''
214 connection_args = get_connection_args(profile)
Adam Tenglere8afccc2017-06-27 17:57:21 +0000215 conn = _authng(profile)
Jiri Broulik70d9e3f2017-02-15 18:37:13 +0100216 nt_ks = conn.compute_conn
217 ret = nt_ks.aggregates.list()
218 return ret
219
220
221def availability_zone_get(name, profile=None):
222 '''
223 list existing availability zones
224 '''
225 connection_args = get_connection_args(profile)
Adam Tenglere8afccc2017-06-27 17:57:21 +0000226 conn = _authng(profile)
Jiri Broulik70d9e3f2017-02-15 18:37:13 +0100227 nt_ks = conn.compute_conn
228 zone_exists=False
229 items = availability_zone_list(profile)
230 for p in items:
231 item = nt_ks.aggregates.get(p).__getattr__('name')
232 if item == name:
233 zone_exists = True
234 return zone_exists
235
236
237def availability_zone_create(name, availability_zone, profile=None):
238 '''
239 create availability zone
240 '''
241 connection_args = get_connection_args(profile)
Adam Tenglere8afccc2017-06-27 17:57:21 +0000242 conn = _authng(profile)
Jiri Broulik70d9e3f2017-02-15 18:37:13 +0100243 nt_ks = conn.compute_conn
244 item = nt_ks.aggregates.create(name, availability_zone)
245 ret = {
246 'Id': item.__getattr__('id'),
247 'Aggregate Name': item.__getattr__('name'),
248 'Availability Zone': item.__getattr__('availability_zone'),
249 }
250 return ret
Damian Szeluga5dca0f02017-04-13 17:27:15 +0200251
252def aggregate_list(profile=None):
253 '''
254 list existing aggregates
255 '''
256 connection_args = get_connection_args(profile)
Adam Tenglere8afccc2017-06-27 17:57:21 +0000257 conn = _authng(profile)
Damian Szeluga5dca0f02017-04-13 17:27:15 +0200258 nt_ks = conn.compute_conn
259 ret = nt_ks.aggregates.list()
260 return ret
261
262
263def aggregate_get(name, profile=None):
264 '''
265 list existing aggregates
266 '''
267 connection_args = get_connection_args(profile)
Adam Tenglere8afccc2017-06-27 17:57:21 +0000268 conn = _authng(profile)
Damian Szeluga5dca0f02017-04-13 17:27:15 +0200269 nt_ks = conn.compute_conn
270 aggregate_exists=False
271 items = aggregate_list(profile)
272 for p in items:
273 item = nt_ks.aggregates.get(p).__getattr__('name')
274 if item == name:
275 aggregate_exists = True
276 return aggregate_exists
277
278
279def aggregate_create(name, aggregate, profile=None):
280 '''
281 create aggregate
282 '''
283 connection_args = get_connection_args(profile)
Adam Tenglere8afccc2017-06-27 17:57:21 +0000284 conn = _authng(profile)
Damian Szeluga5dca0f02017-04-13 17:27:15 +0200285 nt_ks = conn.compute_conn
286 item = nt_ks.aggregates.create(name, aggregate)
287 ret = {
288 'Id': item.__getattr__('id'),
289 'Aggregate Name': item.__getattr__('name'),
290 }
291 return ret
Adam Tenglere8afccc2017-06-27 17:57:21 +0000292
293#
294# Moved from salt.utils.openstack.nova until this works in upstream
295#
296
297'''
298Nova class
299'''
300
301# Import Python libs
302import inspect
303import time
304
305from distutils.version import LooseVersion as _LooseVersion
306
307# Import third party libs
308import salt.ext.six as six
309HAS_NOVA = False
310# pylint: disable=import-error
311try:
312 import novaclient
313 from novaclient import client
314 from novaclient.shell import OpenStackComputeShell
315 import novaclient.utils
316 import novaclient.exceptions
317 import novaclient.extension
318 import novaclient.base
319 HAS_NOVA = True
320except ImportError:
321 pass
322
323OCATA = True
324try:
325 import novaclient.auth_plugin
326 OCATA = False
327except ImportError:
328 pass
329
330HAS_KEYSTONEAUTH = False
331try:
332 import keystoneauth1.loading
333 import keystoneauth1.session
334 HAS_KEYSTONEAUTH = True
335except ImportError:
336 pass
337# pylint: enable=import-error
338
339# Import salt libs
340import salt.utils
341from salt.exceptions import SaltCloudSystemExit
342
343# Version added to novaclient.client.Client function
344NOVACLIENT_MINVER = '2.6.1'
345
346# dict for block_device_mapping_v2
347CLIENT_BDM2_KEYS = {
348 'id': 'uuid',
349 'source': 'source_type',
350 'dest': 'destination_type',
351 'bus': 'disk_bus',
352 'device': 'device_name',
353 'size': 'volume_size',
354 'format': 'guest_format',
355 'bootindex': 'boot_index',
356 'type': 'device_type',
357 'shutdown': 'delete_on_termination',
358}
359
360
361def check_nova():
362 if HAS_NOVA:
363 novaclient_ver = _LooseVersion(novaclient.__version__)
364 min_ver = _LooseVersion(NOVACLIENT_MINVER)
365 if novaclient_ver >= min_ver:
366 return HAS_NOVA
367 log.debug('Newer novaclient version required. Minimum: {0}'.format(NOVACLIENT_MINVER))
368 return False
369
370
371# kwargs has to be an object instead of a dictionary for the __post_parse_arg__
372class KwargsStruct(object):
373 def __init__(self, **entries):
374 self.__dict__.update(entries)
375
376
377def _parse_block_device_mapping_v2(block_device=None, boot_volume=None, snapshot=None, ephemeral=None, swap=None):
378 bdm = []
379 if block_device is None:
380 block_device = []
381 if ephemeral is None:
382 ephemeral = []
383
384 if boot_volume is not None:
385 bdm_dict = {'uuid': boot_volume, 'source_type': 'volume',
386 'destination_type': 'volume', 'boot_index': 0,
387 'delete_on_termination': False}
388 bdm.append(bdm_dict)
389
390 if snapshot is not None:
391 bdm_dict = {'uuid': snapshot, 'source_type': 'snapshot',
392 'destination_type': 'volume', 'boot_index': 0,
393 'delete_on_termination': False}
394 bdm.append(bdm_dict)
395
396 for device_spec in block_device:
397 bdm_dict = {}
398
399 for key, value in six.iteritems(device_spec):
400 bdm_dict[CLIENT_BDM2_KEYS[key]] = value
401
402 # Convert the delete_on_termination to a boolean or set it to true by
403 # default for local block devices when not specified.
404 if 'delete_on_termination' in bdm_dict:
405 action = bdm_dict['delete_on_termination']
406 bdm_dict['delete_on_termination'] = (action == 'remove')
407 elif bdm_dict.get('destination_type') == 'local':
408 bdm_dict['delete_on_termination'] = True
409
410 bdm.append(bdm_dict)
411
412 for ephemeral_spec in ephemeral:
413 bdm_dict = {'source_type': 'blank', 'destination_type': 'local',
414 'boot_index': -1, 'delete_on_termination': True}
415 if 'size' in ephemeral_spec:
416 bdm_dict['volume_size'] = ephemeral_spec['size']
417 if 'format' in ephemeral_spec:
418 bdm_dict['guest_format'] = ephemeral_spec['format']
419
420 bdm.append(bdm_dict)
421
422 if swap is not None:
423 bdm_dict = {'source_type': 'blank', 'destination_type': 'local',
424 'boot_index': -1, 'delete_on_termination': True,
425 'guest_format': 'swap', 'volume_size': swap}
426 bdm.append(bdm_dict)
427
428 return bdm
429
430
431class NovaServer(object):
432 def __init__(self, name, server, password=None):
433 '''
434 Make output look like libcloud output for consistency
435 '''
436 self.name = name
437 self.id = server['id']
438 self.image = server.get('image', {}).get('id', 'Boot From Volume')
439 self.size = server['flavor']['id']
440 self.state = server['state']
441 self._uuid = None
442 self.extra = {
443 'metadata': server['metadata'],
444 'access_ip': server['accessIPv4']
445 }
446
447 self.addresses = server.get('addresses', {})
448 self.public_ips, self.private_ips = [], []
449 for network in self.addresses.values():
450 for addr in network:
451 if salt.utils.cloud.is_public_ip(addr['addr']):
452 self.public_ips.append(addr['addr'])
453 else:
454 self.private_ips.append(addr['addr'])
455
456 if password:
457 self.extra['password'] = password
458
459 def __str__(self):
460 return self.__dict__
461
462
463def get_entry(dict_, key, value, raise_error=True):
464 for entry in dict_:
465 if entry[key] == value:
466 return entry
467 if raise_error is True:
468 raise SaltCloudSystemExit('Unable to find {0} in {1}.'.format(key, dict_))
469 return {}
470
471
472def get_entry_multi(dict_, pairs, raise_error=True):
473 for entry in dict_:
474 if all([entry[key] == value for key, value in pairs]):
475 return entry
476 if raise_error is True:
477 raise SaltCloudSystemExit('Unable to find {0} in {1}.'.format(pairs, dict_))
478 return {}
479
480
481def sanatize_novaclient(kwargs):
482 variables = (
483 'username', 'api_key', 'project_id', 'auth_url', 'insecure',
484 'timeout', 'proxy_tenant_id', 'proxy_token', 'region_name',
485 'endpoint_type', 'extensions', 'service_type', 'service_name',
486 'volume_service_name', 'timings', 'bypass_url', 'os_cache',
487 'no_cache', 'http_log_debug', 'auth_system', 'auth_plugin',
488 'auth_token', 'cacert', 'tenant_id'
489 )
490 ret = {}
491 for var in kwargs:
492 if var in variables:
493 ret[var] = kwargs[var]
494
495 return ret
496
497
Adam Tenglerfac39bf2017-06-28 11:01:58 +0000498def _format_v2_endpoints(endpoints_v2, services):
499 catalog = []
500 for endpoint_v2 in endpoints_v2:
501 endpoints = []
502 endpoint = endpoint_v2.copy()
503 if 'internalurl' in endpoint:
504 internalurl = endpoint.pop('internalurl')
505 endpoint['internalURL'] = internalurl
506
507 if 'adminurl' in endpoint:
508 adminurl = endpoint.pop('adminurl')
509 endpoint['adminURL'] = adminurl
510
511 if 'publicurl' in endpoint:
512 publicurl = endpoint.pop('publicurl')
513 endpoint['publicURL'] = publicurl
514
515 etype = endpoint.pop('type', '')
516 ename = endpoint.pop('name', '')
517 if endpoint.get('service_id', None) and not etype and not ename:
518 service = [s for s in services if s.get('id', '') == endpoint.get('service_id')]
519 etype = service[0].get('type', '')
520 ename = service[0].get('name', '')
521
522 entry = {
523 'type': etype,
524 'name': ename,
525 'id': endpoint.pop('id'),
526 'region': endpoint.get('region'),
527 'endpoints': [endpoint]
528 }
529 catalog.append(entry)
530
531 return catalog
532
533
Adam Tenglere8afccc2017-06-27 17:57:21 +0000534# Function alias to not shadow built-ins
535class SaltNova(object):
536 '''
537 Class for all novaclient functions
538 '''
539 extensions = []
540
541 def __init__(
542 self,
543 username,
544 project_id,
545 auth_url,
546 region_name=None,
547 password=None,
548 os_auth_plugin=None,
549 use_keystoneauth=False,
550 verify=True,
Oleg Iurchenko6ba00f42018-02-21 13:45:49 +0200551 profile=None,
Adam Tenglere8afccc2017-06-27 17:57:21 +0000552 **kwargs
553 ):
554 '''
555 Set up nova credentials
556 '''
Adam Tenglere8afccc2017-06-27 17:57:21 +0000557
Oleg Iurchenko6ba00f42018-02-21 13:45:49 +0200558 self._keystoneng_init(profile=profile, **kwargs)
Adam Tenglere8afccc2017-06-27 17:57:21 +0000559
Oleg Iurchenko6ba00f42018-02-21 13:45:49 +0200560 def _keystoneng_init(self, profile, **kwargs):
561 kstone = __salt__['keystoneng.auth'](profile, **kwargs)
562 self.session = kstone.session
Adam Tenglere8afccc2017-06-27 17:57:21 +0000563 self.version = str(kwargs.get('version', 2))
Oleg Iurchenko6ba00f42018-02-21 13:45:49 +0200564 self.compute_conn = client.Client(version=self.version, session=self.session)
565 self.volume_conn = client.Client(version=self.version, session=self.session)
Adam Tenglere8afccc2017-06-27 17:57:21 +0000566
567 def expand_extensions(self):
568 for connection in (self.compute_conn, self.volume_conn):
569 if connection is None:
570 continue
571 for extension in self.extensions:
572 for attr in extension.module.__dict__:
573 if not inspect.isclass(getattr(extension.module, attr)):
574 continue
575 for key, value in six.iteritems(connection.__dict__):
576 if not isinstance(value, novaclient.base.Manager):
577 continue
578 if value.__class__.__name__ == attr:
579 setattr(connection, key, extension.manager_class(connection))
580
581 def get_catalog(self):
582 '''
583 Return service catalog
584 '''
585 return self.catalog
586
587 def server_show_libcloud(self, uuid):
588 '''
589 Make output look like libcloud output for consistency
590 '''
591 server_info = self.server_show(uuid)
592 server = next(six.itervalues(server_info))
593 server_name = next(six.iterkeys(server_info))
594 if not hasattr(self, 'password'):
595 self.password = None
596 ret = NovaServer(server_name, server, self.password)
597
598 return ret
599
600 def boot(self, name, flavor_id=0, image_id=0, timeout=300, **kwargs):
601 '''
602 Boot a cloud server.
603 '''
604 nt_ks = self.compute_conn
605 kwargs['name'] = name
606 kwargs['flavor'] = flavor_id
607 kwargs['image'] = image_id or None
608 ephemeral = kwargs.pop('ephemeral', [])
609 block_device = kwargs.pop('block_device', [])
610 boot_volume = kwargs.pop('boot_volume', None)
611 snapshot = kwargs.pop('snapshot', None)
612 swap = kwargs.pop('swap', None)
613 kwargs['block_device_mapping_v2'] = _parse_block_device_mapping_v2(
614 block_device=block_device, boot_volume=boot_volume, snapshot=snapshot,
615 ephemeral=ephemeral, swap=swap
616 )
617 response = nt_ks.servers.create(**kwargs)
618 self.uuid = response.id
619 self.password = getattr(response, 'adminPass', None)
620
621 start = time.time()
622 trycount = 0
623 while True:
624 trycount += 1
625 try:
626 return self.server_show_libcloud(self.uuid)
627 except Exception as exc:
628 log.debug(
629 'Server information not yet available: {0}'.format(exc)
630 )
631 time.sleep(1)
632 if time.time() - start > timeout:
633 log.error('Timed out after {0} seconds '
634 'while waiting for data'.format(timeout))
635 return False
636
637 log.debug(
638 'Retrying server_show() (try {0})'.format(trycount)
639 )
640
641 def show_instance(self, name):
642 '''
643 Find a server by its name (libcloud)
644 '''
645 return self.server_by_name(name)
646
647 def root_password(self, server_id, password):
648 '''
649 Change server(uuid's) root password
650 '''
651 nt_ks = self.compute_conn
652 nt_ks.servers.change_password(server_id, password)
653
654 def server_by_name(self, name):
655 '''
656 Find a server by its name
657 '''
658 return self.server_show_libcloud(
659 self.server_list().get(name, {}).get('id', '')
660 )
661
662 def _volume_get(self, volume_id):
663 '''
664 Organize information about a volume from the volume_id
665 '''
666 if self.volume_conn is None:
667 raise SaltCloudSystemExit('No cinder endpoint available')
668 nt_ks = self.volume_conn
669 volume = nt_ks.volumes.get(volume_id)
670 response = {'name': volume.display_name,
671 'size': volume.size,
672 'id': volume.id,
673 'description': volume.display_description,
674 'attachments': volume.attachments,
675 'status': volume.status
676 }
677 return response
678
679 def volume_list(self, search_opts=None):
680 '''
681 List all block volumes
682 '''
683 if self.volume_conn is None:
684 raise SaltCloudSystemExit('No cinder endpoint available')
685 nt_ks = self.volume_conn
686 volumes = nt_ks.volumes.list(search_opts=search_opts)
687 response = {}
688 for volume in volumes:
689 response[volume.display_name] = {
690 'name': volume.display_name,
691 'size': volume.size,
692 'id': volume.id,
693 'description': volume.display_description,
694 'attachments': volume.attachments,
695 'status': volume.status
696 }
697 return response
698
699 def volume_show(self, name):
700 '''
701 Show one volume
702 '''
703 if self.volume_conn is None:
704 raise SaltCloudSystemExit('No cinder endpoint available')
705 nt_ks = self.volume_conn
706 volumes = self.volume_list(
707 search_opts={'display_name': name},
708 )
709 volume = volumes[name]
710# except Exception as esc:
711# # volume doesn't exist
712# log.error(esc.strerror)
713# return {'name': name, 'status': 'deleted'}
714
715 return volume
716
717 def volume_create(self, name, size=100, snapshot=None, voltype=None,
718 availability_zone=None):
719 '''
720 Create a block device
721 '''
722 if self.volume_conn is None:
723 raise SaltCloudSystemExit('No cinder endpoint available')
724 nt_ks = self.volume_conn
725 response = nt_ks.volumes.create(
726 size=size,
727 display_name=name,
728 volume_type=voltype,
729 snapshot_id=snapshot,
730 availability_zone=availability_zone
731 )
732
733 return self._volume_get(response.id)
734
735 def volume_delete(self, name):
736 '''
737 Delete a block device
738 '''
739 if self.volume_conn is None:
740 raise SaltCloudSystemExit('No cinder endpoint available')
741 nt_ks = self.volume_conn
742 try:
743 volume = self.volume_show(name)
744 except KeyError as exc:
745 raise SaltCloudSystemExit('Unable to find {0} volume: {1}'.format(name, exc))
746 if volume['status'] == 'deleted':
747 return volume
748 response = nt_ks.volumes.delete(volume['id'])
749 return volume
750
751 def volume_detach(self,
752 name,
753 timeout=300):
754 '''
755 Detach a block device
756 '''
757 try:
758 volume = self.volume_show(name)
759 except KeyError as exc:
760 raise SaltCloudSystemExit('Unable to find {0} volume: {1}'.format(name, exc))
761 if not volume['attachments']:
762 return True
763 response = self.compute_conn.volumes.delete_server_volume(
764 volume['attachments'][0]['server_id'],
765 volume['attachments'][0]['id']
766 )
767 trycount = 0
768 start = time.time()
769 while True:
770 trycount += 1
771 try:
772 response = self._volume_get(volume['id'])
773 if response['status'] == 'available':
774 return response
775 except Exception as exc:
776 log.debug('Volume is detaching: {0}'.format(name))
777 time.sleep(1)
778 if time.time() - start > timeout:
779 log.error('Timed out after {0} seconds '
780 'while waiting for data'.format(timeout))
781 return False
782
783 log.debug(
784 'Retrying volume_show() (try {0})'.format(trycount)
785 )
786
787 def volume_attach(self,
788 name,
789 server_name,
790 device='/dev/xvdb',
791 timeout=300):
792 '''
793 Attach a block device
794 '''
795 try:
796 volume = self.volume_show(name)
797 except KeyError as exc:
798 raise SaltCloudSystemExit('Unable to find {0} volume: {1}'.format(name, exc))
799 server = self.server_by_name(server_name)
800 response = self.compute_conn.volumes.create_server_volume(
801 server.id,
802 volume['id'],
803 device=device
804 )
805 trycount = 0
806 start = time.time()
807 while True:
808 trycount += 1
809 try:
810 response = self._volume_get(volume['id'])
811 if response['status'] == 'in-use':
812 return response
813 except Exception as exc:
814 log.debug('Volume is attaching: {0}'.format(name))
815 time.sleep(1)
816 if time.time() - start > timeout:
817 log.error('Timed out after {0} seconds '
818 'while waiting for data'.format(timeout))
819 return False
820
821 log.debug(
822 'Retrying volume_show() (try {0})'.format(trycount)
823 )
824
825 def suspend(self, instance_id):
826 '''
827 Suspend a server
828 '''
829 nt_ks = self.compute_conn
830 response = nt_ks.servers.suspend(instance_id)
831 return True
832
833 def resume(self, instance_id):
834 '''
835 Resume a server
836 '''
837 nt_ks = self.compute_conn
838 response = nt_ks.servers.resume(instance_id)
839 return True
840
841 def lock(self, instance_id):
842 '''
843 Lock an instance
844 '''
845 nt_ks = self.compute_conn
846 response = nt_ks.servers.lock(instance_id)
847 return True
848
849 def delete(self, instance_id):
850 '''
851 Delete a server
852 '''
853 nt_ks = self.compute_conn
854 response = nt_ks.servers.delete(instance_id)
855 return True
856
857 def flavor_list(self):
858 '''
859 Return a list of available flavors (nova flavor-list)
860 '''
861 nt_ks = self.compute_conn
862 ret = {}
863 for flavor in nt_ks.flavors.list():
864 links = {}
865 for link in flavor.links:
866 links[link['rel']] = link['href']
867 ret[flavor.name] = {
868 'disk': flavor.disk,
869 'id': flavor.id,
870 'name': flavor.name,
871 'ram': flavor.ram,
872 'swap': flavor.swap,
873 'vcpus': flavor.vcpus,
874 'links': links,
875 }
876 if hasattr(flavor, 'rxtx_factor'):
877 ret[flavor.name]['rxtx_factor'] = flavor.rxtx_factor
878 return ret
879
880 list_sizes = flavor_list
881
882 def flavor_create(self,
883 name, # pylint: disable=C0103
884 flavor_id=0, # pylint: disable=C0103
885 ram=0,
886 disk=0,
887 vcpus=1):
888 '''
889 Create a flavor
890 '''
891 nt_ks = self.compute_conn
892 nt_ks.flavors.create(
893 name=name, flavorid=flavor_id, ram=ram, disk=disk, vcpus=vcpus
894 )
895 return {'name': name,
896 'id': flavor_id,
897 'ram': ram,
898 'disk': disk,
899 'vcpus': vcpus}
900
901 def flavor_delete(self, flavor_id): # pylint: disable=C0103
902 '''
903 Delete a flavor
904 '''
905 nt_ks = self.compute_conn
906 nt_ks.flavors.delete(flavor_id)
907 return 'Flavor deleted: {0}'.format(flavor_id)
908
909 def keypair_list(self):
910 '''
911 List keypairs
912 '''
913 nt_ks = self.compute_conn
914 ret = {}
915 for keypair in nt_ks.keypairs.list():
916 ret[keypair.name] = {
917 'name': keypair.name,
918 'fingerprint': keypair.fingerprint,
919 'public_key': keypair.public_key,
920 }
921 return ret
922
923 def keypair_add(self, name, pubfile=None, pubkey=None):
924 '''
925 Add a keypair
926 '''
927 nt_ks = self.compute_conn
928 if pubfile:
929 with salt.utils.fopen(pubfile, 'r') as fp_:
930 pubkey = fp_.read()
931 if not pubkey:
932 return False
933 nt_ks.keypairs.create(name, public_key=pubkey)
934 ret = {'name': name, 'pubkey': pubkey}
935 return ret
936
937 def keypair_delete(self, name):
938 '''
939 Delete a keypair
940 '''
941 nt_ks = self.compute_conn
942 nt_ks.keypairs.delete(name)
943 return 'Keypair deleted: {0}'.format(name)
944
945 def image_show(self, image_id):
946 '''
947 Show image details and metadata
948 '''
949 nt_ks = self.compute_conn
950 image = nt_ks.images.get(image_id)
951 links = {}
952 for link in image.links:
953 links[link['rel']] = link['href']
954 ret = {
955 'name': image.name,
956 'id': image.id,
957 'status': image.status,
958 'progress': image.progress,
959 'created': image.created,
960 'updated': image.updated,
961 'metadata': image.metadata,
962 'links': links,
963 }
964 if hasattr(image, 'minDisk'):
965 ret['minDisk'] = image.minDisk
966 if hasattr(image, 'minRam'):
967 ret['minRam'] = image.minRam
968
969 return ret
970
971 def image_list(self, name=None):
972 '''
973 List server images
974 '''
975 nt_ks = self.compute_conn
976 ret = {}
977 for image in nt_ks.images.list():
978 links = {}
979 for link in image.links:
980 links[link['rel']] = link['href']
981 ret[image.name] = {
982 'name': image.name,
983 'id': image.id,
984 'status': image.status,
985 'progress': image.progress,
986 'created': image.created,
987 'updated': image.updated,
988 'metadata': image.metadata,
989 'links': links,
990 }
991 if hasattr(image, 'minDisk'):
992 ret[image.name]['minDisk'] = image.minDisk
993 if hasattr(image, 'minRam'):
994 ret[image.name]['minRam'] = image.minRam
995 if name:
996 return {name: ret[name]}
997 return ret
998
999 list_images = image_list
1000
1001 def image_meta_set(self,
1002 image_id=None,
1003 name=None,
1004 **kwargs): # pylint: disable=C0103
1005 '''
1006 Set image metadata
1007 '''
1008 nt_ks = self.compute_conn
1009 if name:
1010 for image in nt_ks.images.list():
1011 if image.name == name:
1012 image_id = image.id # pylint: disable=C0103
1013 if not image_id:
1014 return {'Error': 'A valid image name or id was not specified'}
1015 nt_ks.images.set_meta(image_id, kwargs)
1016 return {image_id: kwargs}
1017
1018 def image_meta_delete(self,
1019 image_id=None, # pylint: disable=C0103
1020 name=None,
1021 keys=None):
1022 '''
1023 Delete image metadata
1024 '''
1025 nt_ks = self.compute_conn
1026 if name:
1027 for image in nt_ks.images.list():
1028 if image.name == name:
1029 image_id = image.id # pylint: disable=C0103
1030 pairs = keys.split(',')
1031 if not image_id:
1032 return {'Error': 'A valid image name or id was not specified'}
1033 nt_ks.images.delete_meta(image_id, pairs)
1034 return {image_id: 'Deleted: {0}'.format(pairs)}
1035
1036 def server_list(self):
1037 '''
1038 List servers
1039 '''
1040 nt_ks = self.compute_conn
1041 ret = {}
1042 for item in nt_ks.servers.list():
1043 try:
1044 ret[item.name] = {
1045 'id': item.id,
1046 'name': item.name,
1047 'state': item.status,
1048 'accessIPv4': item.accessIPv4,
1049 'accessIPv6': item.accessIPv6,
1050 'flavor': {'id': item.flavor['id'],
1051 'links': item.flavor['links']},
1052 'image': {'id': item.image['id'] if item.image else 'Boot From Volume',
1053 'links': item.image['links'] if item.image else ''},
1054 }
1055 except TypeError:
1056 pass
1057 return ret
1058
1059 def server_list_min(self):
1060 '''
1061 List minimal information about servers
1062 '''
1063 nt_ks = self.compute_conn
1064 ret = {}
1065 for item in nt_ks.servers.list(detailed=False):
1066 try:
1067 ret[item.name] = {
1068 'id': item.id,
1069 'status': 'Running'
1070 }
1071 except TypeError:
1072 pass
1073 return ret
1074
1075 def server_list_detailed(self):
1076 '''
1077 Detailed list of servers
1078 '''
1079 nt_ks = self.compute_conn
1080 ret = {}
1081 for item in nt_ks.servers.list():
1082 try:
1083 ret[item.name] = {
1084 'OS-EXT-SRV-ATTR': {},
1085 'OS-EXT-STS': {},
1086 'accessIPv4': item.accessIPv4,
1087 'accessIPv6': item.accessIPv6,
1088 'addresses': item.addresses,
1089 'created': item.created,
1090 'flavor': {'id': item.flavor['id'],
1091 'links': item.flavor['links']},
1092 'hostId': item.hostId,
1093 'id': item.id,
1094 'image': {'id': item.image['id'] if item.image else 'Boot From Volume',
1095 'links': item.image['links'] if item.image else ''},
1096 'key_name': item.key_name,
1097 'links': item.links,
1098 'metadata': item.metadata,
1099 'name': item.name,
1100 'state': item.status,
1101 'tenant_id': item.tenant_id,
1102 'updated': item.updated,
1103 'user_id': item.user_id,
1104 }
1105 except TypeError:
1106 continue
1107
1108 ret[item.name]['progress'] = getattr(item, 'progress', '0')
1109
1110 if hasattr(item.__dict__, 'OS-DCF:diskConfig'):
1111 ret[item.name]['OS-DCF'] = {
1112 'diskConfig': item.__dict__['OS-DCF:diskConfig']
1113 }
1114 if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:host'):
1115 ret[item.name]['OS-EXT-SRV-ATTR']['host'] = \
1116 item.__dict__['OS-EXT-SRV-ATTR:host']
1117 if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:hypervisor_hostname'):
1118 ret[item.name]['OS-EXT-SRV-ATTR']['hypervisor_hostname'] = \
1119 item.__dict__['OS-EXT-SRV-ATTR:hypervisor_hostname']
1120 if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:instance_name'):
1121 ret[item.name]['OS-EXT-SRV-ATTR']['instance_name'] = \
1122 item.__dict__['OS-EXT-SRV-ATTR:instance_name']
1123 if hasattr(item.__dict__, 'OS-EXT-STS:power_state'):
1124 ret[item.name]['OS-EXT-STS']['power_state'] = \
1125 item.__dict__['OS-EXT-STS:power_state']
1126 if hasattr(item.__dict__, 'OS-EXT-STS:task_state'):
1127 ret[item.name]['OS-EXT-STS']['task_state'] = \
1128 item.__dict__['OS-EXT-STS:task_state']
1129 if hasattr(item.__dict__, 'OS-EXT-STS:vm_state'):
1130 ret[item.name]['OS-EXT-STS']['vm_state'] = \
1131 item.__dict__['OS-EXT-STS:vm_state']
1132 if hasattr(item.__dict__, 'security_groups'):
1133 ret[item.name]['security_groups'] = \
1134 item.__dict__['security_groups']
1135 return ret
1136
1137 def server_show(self, server_id):
1138 '''
1139 Show details of one server
1140 '''
1141 ret = {}
1142 try:
1143 servers = self.server_list_detailed()
1144 except AttributeError:
1145 raise SaltCloudSystemExit('Corrupt server in server_list_detailed. Remove corrupt servers.')
1146 for server_name, server in six.iteritems(servers):
1147 if str(server['id']) == server_id:
1148 ret[server_name] = server
1149 return ret
1150
1151 def secgroup_create(self, name, description):
1152 '''
1153 Create a security group
1154 '''
1155 nt_ks = self.compute_conn
1156 nt_ks.security_groups.create(name, description)
1157 ret = {'name': name, 'description': description}
1158 return ret
1159
1160 def secgroup_delete(self, name):
1161 '''
1162 Delete a security group
1163 '''
1164 nt_ks = self.compute_conn
1165 for item in nt_ks.security_groups.list():
1166 if item.name == name:
1167 nt_ks.security_groups.delete(item.id)
1168 return {name: 'Deleted security group: {0}'.format(name)}
1169 return 'Security group not found: {0}'.format(name)
1170
1171 def secgroup_list(self):
1172 '''
1173 List security groups
1174 '''
1175 nt_ks = self.compute_conn
1176 ret = {}
1177 for item in nt_ks.security_groups.list():
1178 ret[item.name] = {
1179 'name': item.name,
1180 'description': item.description,
1181 'id': item.id,
1182 'tenant_id': item.tenant_id,
1183 'rules': item.rules,
1184 }
1185 return ret
1186
1187 def _item_list(self):
1188 '''
1189 List items
1190 '''
1191 nt_ks = self.compute_conn
1192 ret = []
1193 for item in nt_ks.items.list():
1194 ret.append(item.__dict__)
1195 return ret
1196
1197 def _network_show(self, name, network_lst):
1198 '''
1199 Parse the returned network list
1200 '''
1201 for net in network_lst:
1202 if net.label == name:
1203 return net.__dict__
1204 return {}
1205
1206 def network_show(self, name):
1207 '''
1208 Show network information
1209 '''
1210 nt_ks = self.compute_conn
1211 net_list = nt_ks.networks.list()
1212 return self._network_show(name, net_list)
1213
1214 def network_list(self):
1215 '''
1216 List extra private networks
1217 '''
1218 nt_ks = self.compute_conn
1219 return [network.__dict__ for network in nt_ks.networks.list()]
1220
1221 def _sanatize_network_params(self, kwargs):
1222 '''
1223 Sanatize novaclient network parameters
1224 '''
1225 params = [
1226 'label', 'bridge', 'bridge_interface', 'cidr', 'cidr_v6', 'dns1',
1227 'dns2', 'fixed_cidr', 'gateway', 'gateway_v6', 'multi_host',
1228 'priority', 'project_id', 'vlan_start', 'vpn_start'
1229 ]
1230
1231 for variable in six.iterkeys(kwargs): # iterate over a copy, we might delete some
1232 if variable not in params:
1233 del kwargs[variable]
1234 return kwargs
1235
1236 def network_create(self, name, **kwargs):
1237 '''
1238 Create extra private network
1239 '''
1240 nt_ks = self.compute_conn
1241 kwargs['label'] = name
1242 kwargs = self._sanatize_network_params(kwargs)
1243 net = nt_ks.networks.create(**kwargs)
1244 return net.__dict__
1245
1246 def _server_uuid_from_name(self, name):
1247 '''
1248 Get server uuid from name
1249 '''
1250 return self.server_list().get(name, {}).get('id', '')
1251
1252 def virtual_interface_list(self, name):
1253 '''
1254 Get virtual interfaces on slice
1255 '''
1256 nt_ks = self.compute_conn
1257 nets = nt_ks.virtual_interfaces.list(self._server_uuid_from_name(name))
1258 return [network.__dict__ for network in nets]
1259
1260 def virtual_interface_create(self, name, net_name):
1261 '''
1262 Add an interfaces to a slice
1263 '''
1264 nt_ks = self.compute_conn
1265 serverid = self._server_uuid_from_name(name)
1266 networkid = self.network_show(net_name).get('id', None)
1267 if networkid is None:
1268 return {net_name: False}
1269 nets = nt_ks.virtual_interfaces.create(networkid, serverid)
1270 return nets
1271
1272 def floating_ip_pool_list(self):
1273 '''
1274 List all floating IP pools
1275 .. versionadded:: 2016.3.0
1276 '''
1277 nt_ks = self.compute_conn
1278 pools = nt_ks.floating_ip_pools.list()
1279 response = {}
1280 for pool in pools:
1281 response[pool.name] = {
1282 'name': pool.name,
1283 }
1284 return response
1285
1286 def floating_ip_list(self):
1287 '''
1288 List floating IPs
1289 .. versionadded:: 2016.3.0
1290 '''
1291 nt_ks = self.compute_conn
1292 floating_ips = nt_ks.floating_ips.list()
1293 response = {}
1294 for floating_ip in floating_ips:
1295 response[floating_ip.ip] = {
1296 'ip': floating_ip.ip,
1297 'fixed_ip': floating_ip.fixed_ip,
1298 'id': floating_ip.id,
1299 'instance_id': floating_ip.instance_id,
1300 'pool': floating_ip.pool
1301 }
1302 return response
1303
1304 def floating_ip_show(self, ip):
1305 '''
1306 Show info on specific floating IP
1307 .. versionadded:: 2016.3.0
1308 '''
1309 nt_ks = self.compute_conn
1310 floating_ips = nt_ks.floating_ips.list()
1311 for floating_ip in floating_ips:
1312 if floating_ip.ip == ip:
1313 return floating_ip
1314 return {}
1315
1316 def floating_ip_create(self, pool=None):
1317 '''
1318 Allocate a floating IP
1319 .. versionadded:: 2016.3.0
1320 '''
1321 nt_ks = self.compute_conn
1322 floating_ip = nt_ks.floating_ips.create(pool)
1323 response = {
1324 'ip': floating_ip.ip,
1325 'fixed_ip': floating_ip.fixed_ip,
1326 'id': floating_ip.id,
1327 'instance_id': floating_ip.instance_id,
1328 'pool': floating_ip.pool
1329 }
1330 return response
1331
1332 def floating_ip_delete(self, floating_ip):
1333 '''
1334 De-allocate a floating IP
1335 .. versionadded:: 2016.3.0
1336 '''
1337 ip = self.floating_ip_show(floating_ip)
1338 nt_ks = self.compute_conn
1339 return nt_ks.floating_ips.delete(ip)
1340
1341 def floating_ip_associate(self, server_name, floating_ip):
1342 '''
1343 Associate floating IP address to server
1344 .. versionadded:: 2016.3.0
1345 '''
1346 nt_ks = self.compute_conn
1347 server_ = self.server_by_name(server_name)
1348 server = nt_ks.servers.get(server_.__dict__['id'])
1349 server.add_floating_ip(floating_ip)
1350 return self.floating_ip_list()[floating_ip]
1351
1352 def floating_ip_disassociate(self, server_name, floating_ip):
1353 '''
1354 Disassociate a floating IP from server
1355 .. versionadded:: 2016.3.0
1356 '''
1357 nt_ks = self.compute_conn
1358 server_ = self.server_by_name(server_name)
1359 server = nt_ks.servers.get(server_.__dict__['id'])
1360 server.remove_floating_ip(floating_ip)
1361 return self.floating_ip_list()[floating_ip]
1362
1363#
1364# Moved from salt.modules.nova until this works in upstream
1365#
1366
1367def _auth(profile=None):
1368 '''
1369 Set up nova credentials
1370 '''
1371 if profile:
1372 credentials = __salt__['config.option'](profile)
1373 user = credentials['keystone.user']
1374 password = credentials['keystone.password']
1375 tenant = credentials['keystone.tenant']
1376 auth_url = credentials['keystone.auth_url']
1377 region_name = credentials.get('keystone.region_name', None)
1378 api_key = credentials.get('keystone.api_key', None)
1379 os_auth_system = credentials.get('keystone.os_auth_system', None)
1380 use_keystoneauth = credentials.get('keystone.use_keystoneauth', False)
1381 verify = credentials.get('keystone.verify', False)
1382 else:
1383 user = __salt__['config.option']('keystone.user')
1384 password = __salt__['config.option']('keystone.password')
1385 tenant = __salt__['config.option']('keystone.tenant')
1386 auth_url = __salt__['config.option']('keystone.auth_url')
1387 region_name = __salt__['config.option']('keystone.region_name')
1388 api_key = __salt__['config.option']('keystone.api_key')
1389 os_auth_system = __salt__['config.option']('keystone.os_auth_system')
1390 use_keystoneauth = __salt__['config.option']('keystone.use_keystoneauth', False)
1391 verify = __salt__['config.option']('keystone.verify', True)
1392
1393 kwargs = {
1394 'username': user,
1395 'password': password,
1396 'api_key': api_key,
1397 'project_id': tenant,
1398 'auth_url': auth_url,
1399 'region_name': region_name,
1400 'os_auth_plugin': os_auth_system,
1401 'use_keystoneauth': use_keystoneauth,
Oleg Iurchenko6ba00f42018-02-21 13:45:49 +02001402 'verify': verify,
1403 'profile': profile
Adam Tenglere8afccc2017-06-27 17:57:21 +00001404 }
1405
1406 return SaltNova(**kwargs)
1407
1408
Ondrej Smolae138c5b2017-11-02 11:38:23 +01001409#def boot(name, flavor_id=0, image_id=0, profile=None, timeout=300):
1410# '''
1411# Boot (create) a new instance
1412# name
1413# Name of the new instance (must be first)
1414# flavor_id
1415# Unique integer ID for the flavor
1416# image_id
1417# Unique integer ID for the image
1418# timeout
1419# How long to wait, after creating the instance, for the provider to
1420# return information about it (default 300 seconds).
1421# .. versionadded:: 2014.1.0
1422# CLI Example:
1423# .. code-block:: bash
1424# salt '*' nova.boot myinstance flavor_id=4596 image_id=2
1425# The flavor_id and image_id are obtained from nova.flavor_list and
1426# nova.image_list
1427# .. code-block:: bash
1428# salt '*' nova.flavor_list
1429# salt '*' nova.image_list
1430# '''
1431# conn = _auth(profile)
1432# return conn.boot(name, flavor_id, image_id, timeout)
Adam Tenglere8afccc2017-06-27 17:57:21 +00001433
1434
1435def volume_list(search_opts=None, profile=None):
1436 '''
1437 List storage volumes
1438 search_opts
1439 Dictionary of search options
1440 profile
1441 Profile to use
1442 CLI Example:
1443 .. code-block:: bash
1444 salt '*' nova.volume_list \
1445 search_opts='{"display_name": "myblock"}' \
1446 profile=openstack
1447 '''
1448 conn = _auth(profile)
1449 return conn.volume_list(search_opts=search_opts)
1450
1451
1452def volume_show(name, profile=None):
1453 '''
1454 Create a block storage volume
1455 name
1456 Name of the volume
1457 profile
1458 Profile to use
1459 CLI Example:
1460 .. code-block:: bash
1461 salt '*' nova.volume_show myblock profile=openstack
1462 '''
1463 conn = _auth(profile)
1464 return conn.volume_show(name)
1465
1466
1467def volume_create(name, size=100, snapshot=None, voltype=None,
1468 profile=None):
1469 '''
1470 Create a block storage volume
1471 name
1472 Name of the new volume (must be first)
1473 size
1474 Volume size
1475 snapshot
1476 Block storage snapshot id
1477 voltype
1478 Type of storage
1479 profile
1480 Profile to build on
1481 CLI Example:
1482 .. code-block:: bash
1483 salt '*' nova.volume_create myblock size=300 profile=openstack
1484 '''
1485 conn = _auth(profile)
1486 return conn.volume_create(
1487 name,
1488 size,
1489 snapshot,
1490 voltype
1491 )
1492
1493
1494def volume_delete(name, profile=None):
1495 '''
1496 Destroy the volume
1497 name
1498 Name of the volume
1499 profile
1500 Profile to build on
1501 CLI Example:
1502 .. code-block:: bash
1503 salt '*' nova.volume_delete myblock profile=openstack
1504 '''
1505 conn = _auth(profile)
1506 return conn.volume_delete(name)
1507
1508
1509def volume_detach(name,
1510 profile=None,
1511 timeout=300):
1512 '''
1513 Attach a block storage volume
1514 name
1515 Name of the new volume to attach
1516 server_name
1517 Name of the server to detach from
1518 profile
1519 Profile to build on
1520 CLI Example:
1521 .. code-block:: bash
1522 salt '*' nova.volume_detach myblock profile=openstack
1523 '''
1524 conn = _auth(profile)
1525 return conn.volume_detach(
1526 name,
1527 timeout
1528 )
1529
1530
1531def volume_attach(name,
1532 server_name,
1533 device='/dev/xvdb',
1534 profile=None,
1535 timeout=300):
1536 '''
1537 Attach a block storage volume
1538 name
1539 Name of the new volume to attach
1540 server_name
1541 Name of the server to attach to
1542 device
1543 Name of the device on the server
1544 profile
1545 Profile to build on
1546 CLI Example:
1547 .. code-block:: bash
1548 salt '*' nova.volume_attach myblock slice.example.com profile=openstack
1549 salt '*' nova.volume_attach myblock server.example.com \
1550 device='/dev/xvdb' profile=openstack
1551 '''
1552 conn = _auth(profile)
1553 return conn.volume_attach(
1554 name,
1555 server_name,
1556 device,
1557 timeout
1558 )
1559
1560
1561def suspend(instance_id, profile=None):
1562 '''
1563 Suspend an instance
1564 instance_id
1565 ID of the instance to be suspended
1566 CLI Example:
1567 .. code-block:: bash
1568 salt '*' nova.suspend 1138
1569 '''
1570 conn = _auth(profile)
1571 return conn.suspend(instance_id)
1572
1573
1574def resume(instance_id, profile=None):
1575 '''
1576 Resume an instance
1577 instance_id
1578 ID of the instance to be resumed
1579 CLI Example:
1580 .. code-block:: bash
1581 salt '*' nova.resume 1138
1582 '''
1583 conn = _auth(profile)
1584 return conn.resume(instance_id)
1585
1586
1587def lock(instance_id, profile=None):
1588 '''
1589 Lock an instance
1590 instance_id
1591 ID of the instance to be locked
1592 CLI Example:
1593 .. code-block:: bash
1594 salt '*' nova.lock 1138
1595 '''
1596 conn = _auth(profile)
1597 return conn.lock(instance_id)
1598
1599
1600def delete(instance_id, profile=None):
1601 '''
1602 Delete an instance
1603 instance_id
1604 ID of the instance to be deleted
1605 CLI Example:
1606 .. code-block:: bash
1607 salt '*' nova.delete 1138
1608 '''
1609 conn = _auth(profile)
1610 return conn.delete(instance_id)
1611
1612
1613def flavor_list(profile=None):
1614 '''
1615 Return a list of available flavors (nova flavor-list)
1616 CLI Example:
1617 .. code-block:: bash
1618 salt '*' nova.flavor_list
1619 '''
1620 conn = _auth(profile)
1621 return conn.flavor_list()
1622
1623
1624def flavor_create(name, # pylint: disable=C0103
1625 flavor_id=0, # pylint: disable=C0103
1626 ram=0,
1627 disk=0,
1628 vcpus=1,
1629 profile=None):
1630 '''
1631 Add a flavor to nova (nova flavor-create). The following parameters are
1632 required:
1633 name
1634 Name of the new flavor (must be first)
1635 flavor_id
1636 Unique integer ID for the new flavor
1637 ram
1638 Memory size in MB
1639 disk
1640 Disk size in GB
1641 vcpus
1642 Number of vcpus
1643 CLI Example:
1644 .. code-block:: bash
1645 salt '*' nova.flavor_create myflavor flavor_id=6 \
1646 ram=4096 disk=10 vcpus=1
1647 '''
1648 conn = _auth(profile)
1649 return conn.flavor_create(
1650 name,
1651 flavor_id,
1652 ram,
1653 disk,
1654 vcpus
1655 )
1656
1657
1658def flavor_delete(flavor_id, profile=None): # pylint: disable=C0103
1659 '''
1660 Delete a flavor from nova by id (nova flavor-delete)
1661 CLI Example:
1662 .. code-block:: bash
1663 salt '*' nova.flavor_delete 7
1664 '''
1665 conn = _auth(profile)
1666 return conn.flavor_delete(flavor_id)
1667
1668
1669def keypair_list(profile=None):
1670 '''
1671 Return a list of available keypairs (nova keypair-list)
1672 CLI Example:
1673 .. code-block:: bash
1674 salt '*' nova.keypair_list
1675 '''
1676 conn = _auth(profile)
1677 return conn.keypair_list()
1678
1679
1680def keypair_add(name, pubfile=None, pubkey=None, profile=None):
1681 '''
1682 Add a keypair to nova (nova keypair-add)
1683 CLI Examples:
1684 .. code-block:: bash
1685 salt '*' nova.keypair_add mykey pubfile='/home/myuser/.ssh/id_rsa.pub'
1686 salt '*' nova.keypair_add mykey pubkey='ssh-rsa <key> myuser@mybox'
1687 '''
1688 conn = _auth(profile)
1689 return conn.keypair_add(
1690 name,
1691 pubfile,
1692 pubkey
1693 )
1694
1695
1696def keypair_delete(name, profile=None):
1697 '''
1698 Add a keypair to nova (nova keypair-delete)
1699 CLI Example:
1700 .. code-block:: bash
1701 salt '*' nova.keypair_delete mykey'
1702 '''
1703 conn = _auth(profile)
1704 return conn.keypair_delete(name)
1705
1706
1707def image_list(name=None, profile=None):
1708 '''
1709 Return a list of available images (nova images-list + nova image-show)
1710 If a name is provided, only that image will be displayed.
1711 CLI Examples:
1712 .. code-block:: bash
1713 salt '*' nova.image_list
1714 salt '*' nova.image_list myimage
1715 '''
1716 conn = _auth(profile)
1717 return conn.image_list(name)
1718
1719
1720def image_meta_set(image_id=None,
1721 name=None,
1722 profile=None,
1723 **kwargs): # pylint: disable=C0103
1724 '''
1725 Sets a key=value pair in the metadata for an image (nova image-meta set)
1726 CLI Examples:
1727 .. code-block:: bash
1728 salt '*' nova.image_meta_set 6f52b2ff-0b31-4d84-8fd1-af45b84824f6 \
1729 cheese=gruyere
1730 salt '*' nova.image_meta_set name=myimage salad=pasta beans=baked
1731 '''
1732 conn = _auth(profile)
1733 return conn.image_meta_set(
1734 image_id,
1735 name,
1736 **kwargs
1737 )
1738
1739
1740def image_meta_delete(image_id=None, # pylint: disable=C0103
1741 name=None,
1742 keys=None,
1743 profile=None):
1744 '''
1745 Delete a key=value pair from the metadata for an image
1746 (nova image-meta set)
1747 CLI Examples:
1748 .. code-block:: bash
1749 salt '*' nova.image_meta_delete \
1750 6f52b2ff-0b31-4d84-8fd1-af45b84824f6 keys=cheese
1751 salt '*' nova.image_meta_delete name=myimage keys=salad,beans
1752 '''
1753 conn = _auth(profile)
1754 return conn.image_meta_delete(
1755 image_id,
1756 name,
1757 keys
1758 )
1759
1760
1761def list_(profile=None):
1762 '''
1763 To maintain the feel of the nova command line, this function simply calls
1764 the server_list function.
1765 CLI Example:
1766 .. code-block:: bash
1767 salt '*' nova.list
1768 '''
1769 return server_list(profile=profile)
1770
1771
Ondrej Smolae138c5b2017-11-02 11:38:23 +01001772#def server_list(profile=None):
1773# '''
1774# Return list of active servers
1775# CLI Example:
1776# .. code-block:: bash
1777# salt '*' nova.server_list
1778# '''
1779# conn = _auth(profile)
1780# return conn.server_list()
Adam Tenglere8afccc2017-06-27 17:57:21 +00001781
1782
1783def show(server_id, profile=None):
1784 '''
1785 To maintain the feel of the nova command line, this function simply calls
1786 the server_show function.
1787 CLI Example:
1788 .. code-block:: bash
1789 salt '*' nova.show
1790 '''
1791 return server_show(server_id, profile)
1792
1793
1794def server_list_detailed(profile=None):
1795 '''
1796 Return detailed list of active servers
1797 CLI Example:
1798 .. code-block:: bash
1799 salt '*' nova.server_list_detailed
1800 '''
1801 conn = _auth(profile)
1802 return conn.server_list_detailed()
1803
1804
1805def server_show(server_id, profile=None):
1806 '''
1807 Return detailed information for an active server
1808 CLI Example:
1809 .. code-block:: bash
1810 salt '*' nova.server_show <server_id>
1811 '''
1812 conn = _auth(profile)
1813 return conn.server_show(server_id)
1814
1815
Ondrej Smolae138c5b2017-11-02 11:38:23 +01001816#def secgroup_create(name, description, profile=None):
1817# '''
1818# Add a secgroup to nova (nova secgroup-create)
1819# CLI Example:
1820# .. code-block:: bash
1821# salt '*' nova.secgroup_create mygroup 'This is my security group'
1822# '''
1823# conn = _auth(profile)
1824# return conn.secgroup_create(name, description)
1825#
1826#
1827#def secgroup_delete(name, profile=None):
1828# '''
1829# Delete a secgroup to nova (nova secgroup-delete)
1830# CLI Example:
1831# .. code-block:: bash
1832# salt '*' nova.secgroup_delete mygroup
1833# '''
1834# conn = _auth(profile)
1835# return conn.secgroup_delete(name)
1836#
1837#
1838#def secgroup_list(profile=None):
1839# '''
1840# Return a list of available security groups (nova items-list)
1841# CLI Example:
1842# .. code-block:: bash
1843# salt '*' nova.secgroup_list
1844# '''
1845# conn = _auth(profile)
1846# return conn.secgroup_list()
Adam Tenglere8afccc2017-06-27 17:57:21 +00001847
1848
1849def server_by_name(name, profile=None):
1850 '''
1851 Return information about a server
1852 name
1853 Server Name
1854 CLI Example:
1855 .. code-block:: bash
1856 salt '*' nova.server_by_name myserver profile=openstack
1857 '''
1858 conn = _auth(profile)
1859 return conn.server_by_name(name)
1860