blob: dbfc29759ea3ba47bb694c520631cbb4263782df [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))
Oleksii Molchanov7b3a39d2020-05-25 12:42:35 +0300564 self.endpoint_type = str(kwargs.get('connection_endpoint_type', 'internal'))
565 self.compute_conn = client.Client(version=self.version,
566 session=self.session,
567 endpoint_type=self.endpoint_type)
568 self.volume_conn = client.Client(version=self.version,
569 session=self.session,
570 endpoint_type=self.endpoint_type)
Adam Tenglere8afccc2017-06-27 17:57:21 +0000571
572 def expand_extensions(self):
573 for connection in (self.compute_conn, self.volume_conn):
574 if connection is None:
575 continue
576 for extension in self.extensions:
577 for attr in extension.module.__dict__:
578 if not inspect.isclass(getattr(extension.module, attr)):
579 continue
580 for key, value in six.iteritems(connection.__dict__):
581 if not isinstance(value, novaclient.base.Manager):
582 continue
583 if value.__class__.__name__ == attr:
584 setattr(connection, key, extension.manager_class(connection))
585
586 def get_catalog(self):
587 '''
588 Return service catalog
589 '''
590 return self.catalog
591
592 def server_show_libcloud(self, uuid):
593 '''
594 Make output look like libcloud output for consistency
595 '''
596 server_info = self.server_show(uuid)
597 server = next(six.itervalues(server_info))
598 server_name = next(six.iterkeys(server_info))
599 if not hasattr(self, 'password'):
600 self.password = None
601 ret = NovaServer(server_name, server, self.password)
602
603 return ret
604
605 def boot(self, name, flavor_id=0, image_id=0, timeout=300, **kwargs):
606 '''
607 Boot a cloud server.
608 '''
609 nt_ks = self.compute_conn
610 kwargs['name'] = name
611 kwargs['flavor'] = flavor_id
612 kwargs['image'] = image_id or None
613 ephemeral = kwargs.pop('ephemeral', [])
614 block_device = kwargs.pop('block_device', [])
615 boot_volume = kwargs.pop('boot_volume', None)
616 snapshot = kwargs.pop('snapshot', None)
617 swap = kwargs.pop('swap', None)
618 kwargs['block_device_mapping_v2'] = _parse_block_device_mapping_v2(
619 block_device=block_device, boot_volume=boot_volume, snapshot=snapshot,
620 ephemeral=ephemeral, swap=swap
621 )
622 response = nt_ks.servers.create(**kwargs)
623 self.uuid = response.id
624 self.password = getattr(response, 'adminPass', None)
625
626 start = time.time()
627 trycount = 0
628 while True:
629 trycount += 1
630 try:
631 return self.server_show_libcloud(self.uuid)
632 except Exception as exc:
633 log.debug(
634 'Server information not yet available: {0}'.format(exc)
635 )
636 time.sleep(1)
637 if time.time() - start > timeout:
638 log.error('Timed out after {0} seconds '
639 'while waiting for data'.format(timeout))
640 return False
641
642 log.debug(
643 'Retrying server_show() (try {0})'.format(trycount)
644 )
645
646 def show_instance(self, name):
647 '''
648 Find a server by its name (libcloud)
649 '''
650 return self.server_by_name(name)
651
652 def root_password(self, server_id, password):
653 '''
654 Change server(uuid's) root password
655 '''
656 nt_ks = self.compute_conn
657 nt_ks.servers.change_password(server_id, password)
658
659 def server_by_name(self, name):
660 '''
661 Find a server by its name
662 '''
663 return self.server_show_libcloud(
664 self.server_list().get(name, {}).get('id', '')
665 )
666
667 def _volume_get(self, volume_id):
668 '''
669 Organize information about a volume from the volume_id
670 '''
671 if self.volume_conn is None:
672 raise SaltCloudSystemExit('No cinder endpoint available')
673 nt_ks = self.volume_conn
674 volume = nt_ks.volumes.get(volume_id)
675 response = {'name': volume.display_name,
676 'size': volume.size,
677 'id': volume.id,
678 'description': volume.display_description,
679 'attachments': volume.attachments,
680 'status': volume.status
681 }
682 return response
683
684 def volume_list(self, search_opts=None):
685 '''
686 List all block volumes
687 '''
688 if self.volume_conn is None:
689 raise SaltCloudSystemExit('No cinder endpoint available')
690 nt_ks = self.volume_conn
691 volumes = nt_ks.volumes.list(search_opts=search_opts)
692 response = {}
693 for volume in volumes:
694 response[volume.display_name] = {
695 'name': volume.display_name,
696 'size': volume.size,
697 'id': volume.id,
698 'description': volume.display_description,
699 'attachments': volume.attachments,
700 'status': volume.status
701 }
702 return response
703
704 def volume_show(self, name):
705 '''
706 Show one volume
707 '''
708 if self.volume_conn is None:
709 raise SaltCloudSystemExit('No cinder endpoint available')
710 nt_ks = self.volume_conn
711 volumes = self.volume_list(
712 search_opts={'display_name': name},
713 )
714 volume = volumes[name]
715# except Exception as esc:
716# # volume doesn't exist
717# log.error(esc.strerror)
718# return {'name': name, 'status': 'deleted'}
719
720 return volume
721
722 def volume_create(self, name, size=100, snapshot=None, voltype=None,
723 availability_zone=None):
724 '''
725 Create a block device
726 '''
727 if self.volume_conn is None:
728 raise SaltCloudSystemExit('No cinder endpoint available')
729 nt_ks = self.volume_conn
730 response = nt_ks.volumes.create(
731 size=size,
732 display_name=name,
733 volume_type=voltype,
734 snapshot_id=snapshot,
735 availability_zone=availability_zone
736 )
737
738 return self._volume_get(response.id)
739
740 def volume_delete(self, name):
741 '''
742 Delete a block device
743 '''
744 if self.volume_conn is None:
745 raise SaltCloudSystemExit('No cinder endpoint available')
746 nt_ks = self.volume_conn
747 try:
748 volume = self.volume_show(name)
749 except KeyError as exc:
750 raise SaltCloudSystemExit('Unable to find {0} volume: {1}'.format(name, exc))
751 if volume['status'] == 'deleted':
752 return volume
753 response = nt_ks.volumes.delete(volume['id'])
754 return volume
755
756 def volume_detach(self,
757 name,
758 timeout=300):
759 '''
760 Detach a block device
761 '''
762 try:
763 volume = self.volume_show(name)
764 except KeyError as exc:
765 raise SaltCloudSystemExit('Unable to find {0} volume: {1}'.format(name, exc))
766 if not volume['attachments']:
767 return True
768 response = self.compute_conn.volumes.delete_server_volume(
769 volume['attachments'][0]['server_id'],
770 volume['attachments'][0]['id']
771 )
772 trycount = 0
773 start = time.time()
774 while True:
775 trycount += 1
776 try:
777 response = self._volume_get(volume['id'])
778 if response['status'] == 'available':
779 return response
780 except Exception as exc:
781 log.debug('Volume is detaching: {0}'.format(name))
782 time.sleep(1)
783 if time.time() - start > timeout:
784 log.error('Timed out after {0} seconds '
785 'while waiting for data'.format(timeout))
786 return False
787
788 log.debug(
789 'Retrying volume_show() (try {0})'.format(trycount)
790 )
791
792 def volume_attach(self,
793 name,
794 server_name,
795 device='/dev/xvdb',
796 timeout=300):
797 '''
798 Attach a block device
799 '''
800 try:
801 volume = self.volume_show(name)
802 except KeyError as exc:
803 raise SaltCloudSystemExit('Unable to find {0} volume: {1}'.format(name, exc))
804 server = self.server_by_name(server_name)
805 response = self.compute_conn.volumes.create_server_volume(
806 server.id,
807 volume['id'],
808 device=device
809 )
810 trycount = 0
811 start = time.time()
812 while True:
813 trycount += 1
814 try:
815 response = self._volume_get(volume['id'])
816 if response['status'] == 'in-use':
817 return response
818 except Exception as exc:
819 log.debug('Volume is attaching: {0}'.format(name))
820 time.sleep(1)
821 if time.time() - start > timeout:
822 log.error('Timed out after {0} seconds '
823 'while waiting for data'.format(timeout))
824 return False
825
826 log.debug(
827 'Retrying volume_show() (try {0})'.format(trycount)
828 )
829
830 def suspend(self, instance_id):
831 '''
832 Suspend a server
833 '''
834 nt_ks = self.compute_conn
835 response = nt_ks.servers.suspend(instance_id)
836 return True
837
838 def resume(self, instance_id):
839 '''
840 Resume a server
841 '''
842 nt_ks = self.compute_conn
843 response = nt_ks.servers.resume(instance_id)
844 return True
845
846 def lock(self, instance_id):
847 '''
848 Lock an instance
849 '''
850 nt_ks = self.compute_conn
851 response = nt_ks.servers.lock(instance_id)
852 return True
853
854 def delete(self, instance_id):
855 '''
856 Delete a server
857 '''
858 nt_ks = self.compute_conn
859 response = nt_ks.servers.delete(instance_id)
860 return True
861
862 def flavor_list(self):
863 '''
864 Return a list of available flavors (nova flavor-list)
865 '''
866 nt_ks = self.compute_conn
867 ret = {}
868 for flavor in nt_ks.flavors.list():
869 links = {}
870 for link in flavor.links:
871 links[link['rel']] = link['href']
872 ret[flavor.name] = {
873 'disk': flavor.disk,
874 'id': flavor.id,
875 'name': flavor.name,
876 'ram': flavor.ram,
877 'swap': flavor.swap,
878 'vcpus': flavor.vcpus,
879 'links': links,
880 }
881 if hasattr(flavor, 'rxtx_factor'):
882 ret[flavor.name]['rxtx_factor'] = flavor.rxtx_factor
883 return ret
884
885 list_sizes = flavor_list
886
887 def flavor_create(self,
888 name, # pylint: disable=C0103
889 flavor_id=0, # pylint: disable=C0103
890 ram=0,
891 disk=0,
892 vcpus=1):
893 '''
894 Create a flavor
895 '''
896 nt_ks = self.compute_conn
897 nt_ks.flavors.create(
898 name=name, flavorid=flavor_id, ram=ram, disk=disk, vcpus=vcpus
899 )
900 return {'name': name,
901 'id': flavor_id,
902 'ram': ram,
903 'disk': disk,
904 'vcpus': vcpus}
905
906 def flavor_delete(self, flavor_id): # pylint: disable=C0103
907 '''
908 Delete a flavor
909 '''
910 nt_ks = self.compute_conn
911 nt_ks.flavors.delete(flavor_id)
912 return 'Flavor deleted: {0}'.format(flavor_id)
913
914 def keypair_list(self):
915 '''
916 List keypairs
917 '''
918 nt_ks = self.compute_conn
919 ret = {}
920 for keypair in nt_ks.keypairs.list():
921 ret[keypair.name] = {
922 'name': keypair.name,
923 'fingerprint': keypair.fingerprint,
924 'public_key': keypair.public_key,
925 }
926 return ret
927
928 def keypair_add(self, name, pubfile=None, pubkey=None):
929 '''
930 Add a keypair
931 '''
932 nt_ks = self.compute_conn
933 if pubfile:
934 with salt.utils.fopen(pubfile, 'r') as fp_:
935 pubkey = fp_.read()
936 if not pubkey:
937 return False
938 nt_ks.keypairs.create(name, public_key=pubkey)
939 ret = {'name': name, 'pubkey': pubkey}
940 return ret
941
942 def keypair_delete(self, name):
943 '''
944 Delete a keypair
945 '''
946 nt_ks = self.compute_conn
947 nt_ks.keypairs.delete(name)
948 return 'Keypair deleted: {0}'.format(name)
949
950 def image_show(self, image_id):
951 '''
952 Show image details and metadata
953 '''
954 nt_ks = self.compute_conn
955 image = nt_ks.images.get(image_id)
956 links = {}
957 for link in image.links:
958 links[link['rel']] = link['href']
959 ret = {
960 'name': image.name,
961 'id': image.id,
962 'status': image.status,
963 'progress': image.progress,
964 'created': image.created,
965 'updated': image.updated,
966 'metadata': image.metadata,
967 'links': links,
968 }
969 if hasattr(image, 'minDisk'):
970 ret['minDisk'] = image.minDisk
971 if hasattr(image, 'minRam'):
972 ret['minRam'] = image.minRam
973
974 return ret
975
976 def image_list(self, name=None):
977 '''
978 List server images
979 '''
980 nt_ks = self.compute_conn
981 ret = {}
982 for image in nt_ks.images.list():
983 links = {}
984 for link in image.links:
985 links[link['rel']] = link['href']
986 ret[image.name] = {
987 'name': image.name,
988 'id': image.id,
989 'status': image.status,
990 'progress': image.progress,
991 'created': image.created,
992 'updated': image.updated,
993 'metadata': image.metadata,
994 'links': links,
995 }
996 if hasattr(image, 'minDisk'):
997 ret[image.name]['minDisk'] = image.minDisk
998 if hasattr(image, 'minRam'):
999 ret[image.name]['minRam'] = image.minRam
1000 if name:
1001 return {name: ret[name]}
1002 return ret
1003
1004 list_images = image_list
1005
1006 def image_meta_set(self,
1007 image_id=None,
1008 name=None,
1009 **kwargs): # pylint: disable=C0103
1010 '''
1011 Set image metadata
1012 '''
1013 nt_ks = self.compute_conn
1014 if name:
1015 for image in nt_ks.images.list():
1016 if image.name == name:
1017 image_id = image.id # pylint: disable=C0103
1018 if not image_id:
1019 return {'Error': 'A valid image name or id was not specified'}
1020 nt_ks.images.set_meta(image_id, kwargs)
1021 return {image_id: kwargs}
1022
1023 def image_meta_delete(self,
1024 image_id=None, # pylint: disable=C0103
1025 name=None,
1026 keys=None):
1027 '''
1028 Delete image metadata
1029 '''
1030 nt_ks = self.compute_conn
1031 if name:
1032 for image in nt_ks.images.list():
1033 if image.name == name:
1034 image_id = image.id # pylint: disable=C0103
1035 pairs = keys.split(',')
1036 if not image_id:
1037 return {'Error': 'A valid image name or id was not specified'}
1038 nt_ks.images.delete_meta(image_id, pairs)
1039 return {image_id: 'Deleted: {0}'.format(pairs)}
1040
1041 def server_list(self):
1042 '''
1043 List servers
1044 '''
1045 nt_ks = self.compute_conn
1046 ret = {}
1047 for item in nt_ks.servers.list():
1048 try:
1049 ret[item.name] = {
1050 'id': item.id,
1051 'name': item.name,
1052 'state': item.status,
1053 'accessIPv4': item.accessIPv4,
1054 'accessIPv6': item.accessIPv6,
1055 'flavor': {'id': item.flavor['id'],
1056 'links': item.flavor['links']},
1057 'image': {'id': item.image['id'] if item.image else 'Boot From Volume',
1058 'links': item.image['links'] if item.image else ''},
1059 }
1060 except TypeError:
1061 pass
1062 return ret
1063
1064 def server_list_min(self):
1065 '''
1066 List minimal information about servers
1067 '''
1068 nt_ks = self.compute_conn
1069 ret = {}
1070 for item in nt_ks.servers.list(detailed=False):
1071 try:
1072 ret[item.name] = {
1073 'id': item.id,
1074 'status': 'Running'
1075 }
1076 except TypeError:
1077 pass
1078 return ret
1079
1080 def server_list_detailed(self):
1081 '''
1082 Detailed list of servers
1083 '''
1084 nt_ks = self.compute_conn
1085 ret = {}
1086 for item in nt_ks.servers.list():
1087 try:
1088 ret[item.name] = {
1089 'OS-EXT-SRV-ATTR': {},
1090 'OS-EXT-STS': {},
1091 'accessIPv4': item.accessIPv4,
1092 'accessIPv6': item.accessIPv6,
1093 'addresses': item.addresses,
1094 'created': item.created,
1095 'flavor': {'id': item.flavor['id'],
1096 'links': item.flavor['links']},
1097 'hostId': item.hostId,
1098 'id': item.id,
1099 'image': {'id': item.image['id'] if item.image else 'Boot From Volume',
1100 'links': item.image['links'] if item.image else ''},
1101 'key_name': item.key_name,
1102 'links': item.links,
1103 'metadata': item.metadata,
1104 'name': item.name,
1105 'state': item.status,
1106 'tenant_id': item.tenant_id,
1107 'updated': item.updated,
1108 'user_id': item.user_id,
1109 }
1110 except TypeError:
1111 continue
1112
1113 ret[item.name]['progress'] = getattr(item, 'progress', '0')
1114
1115 if hasattr(item.__dict__, 'OS-DCF:diskConfig'):
1116 ret[item.name]['OS-DCF'] = {
1117 'diskConfig': item.__dict__['OS-DCF:diskConfig']
1118 }
1119 if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:host'):
1120 ret[item.name]['OS-EXT-SRV-ATTR']['host'] = \
1121 item.__dict__['OS-EXT-SRV-ATTR:host']
1122 if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:hypervisor_hostname'):
1123 ret[item.name]['OS-EXT-SRV-ATTR']['hypervisor_hostname'] = \
1124 item.__dict__['OS-EXT-SRV-ATTR:hypervisor_hostname']
1125 if hasattr(item.__dict__, 'OS-EXT-SRV-ATTR:instance_name'):
1126 ret[item.name]['OS-EXT-SRV-ATTR']['instance_name'] = \
1127 item.__dict__['OS-EXT-SRV-ATTR:instance_name']
1128 if hasattr(item.__dict__, 'OS-EXT-STS:power_state'):
1129 ret[item.name]['OS-EXT-STS']['power_state'] = \
1130 item.__dict__['OS-EXT-STS:power_state']
1131 if hasattr(item.__dict__, 'OS-EXT-STS:task_state'):
1132 ret[item.name]['OS-EXT-STS']['task_state'] = \
1133 item.__dict__['OS-EXT-STS:task_state']
1134 if hasattr(item.__dict__, 'OS-EXT-STS:vm_state'):
1135 ret[item.name]['OS-EXT-STS']['vm_state'] = \
1136 item.__dict__['OS-EXT-STS:vm_state']
1137 if hasattr(item.__dict__, 'security_groups'):
1138 ret[item.name]['security_groups'] = \
1139 item.__dict__['security_groups']
1140 return ret
1141
1142 def server_show(self, server_id):
1143 '''
1144 Show details of one server
1145 '''
1146 ret = {}
1147 try:
1148 servers = self.server_list_detailed()
1149 except AttributeError:
1150 raise SaltCloudSystemExit('Corrupt server in server_list_detailed. Remove corrupt servers.')
1151 for server_name, server in six.iteritems(servers):
1152 if str(server['id']) == server_id:
1153 ret[server_name] = server
1154 return ret
1155
1156 def secgroup_create(self, name, description):
1157 '''
1158 Create a security group
1159 '''
1160 nt_ks = self.compute_conn
1161 nt_ks.security_groups.create(name, description)
1162 ret = {'name': name, 'description': description}
1163 return ret
1164
1165 def secgroup_delete(self, name):
1166 '''
1167 Delete a security group
1168 '''
1169 nt_ks = self.compute_conn
1170 for item in nt_ks.security_groups.list():
1171 if item.name == name:
1172 nt_ks.security_groups.delete(item.id)
1173 return {name: 'Deleted security group: {0}'.format(name)}
1174 return 'Security group not found: {0}'.format(name)
1175
1176 def secgroup_list(self):
1177 '''
1178 List security groups
1179 '''
1180 nt_ks = self.compute_conn
1181 ret = {}
1182 for item in nt_ks.security_groups.list():
1183 ret[item.name] = {
1184 'name': item.name,
1185 'description': item.description,
1186 'id': item.id,
1187 'tenant_id': item.tenant_id,
1188 'rules': item.rules,
1189 }
1190 return ret
1191
1192 def _item_list(self):
1193 '''
1194 List items
1195 '''
1196 nt_ks = self.compute_conn
1197 ret = []
1198 for item in nt_ks.items.list():
1199 ret.append(item.__dict__)
1200 return ret
1201
1202 def _network_show(self, name, network_lst):
1203 '''
1204 Parse the returned network list
1205 '''
1206 for net in network_lst:
1207 if net.label == name:
1208 return net.__dict__
1209 return {}
1210
1211 def network_show(self, name):
1212 '''
1213 Show network information
1214 '''
1215 nt_ks = self.compute_conn
1216 net_list = nt_ks.networks.list()
1217 return self._network_show(name, net_list)
1218
1219 def network_list(self):
1220 '''
1221 List extra private networks
1222 '''
1223 nt_ks = self.compute_conn
1224 return [network.__dict__ for network in nt_ks.networks.list()]
1225
1226 def _sanatize_network_params(self, kwargs):
1227 '''
1228 Sanatize novaclient network parameters
1229 '''
1230 params = [
1231 'label', 'bridge', 'bridge_interface', 'cidr', 'cidr_v6', 'dns1',
1232 'dns2', 'fixed_cidr', 'gateway', 'gateway_v6', 'multi_host',
1233 'priority', 'project_id', 'vlan_start', 'vpn_start'
1234 ]
1235
1236 for variable in six.iterkeys(kwargs): # iterate over a copy, we might delete some
1237 if variable not in params:
1238 del kwargs[variable]
1239 return kwargs
1240
1241 def network_create(self, name, **kwargs):
1242 '''
1243 Create extra private network
1244 '''
1245 nt_ks = self.compute_conn
1246 kwargs['label'] = name
1247 kwargs = self._sanatize_network_params(kwargs)
1248 net = nt_ks.networks.create(**kwargs)
1249 return net.__dict__
1250
1251 def _server_uuid_from_name(self, name):
1252 '''
1253 Get server uuid from name
1254 '''
1255 return self.server_list().get(name, {}).get('id', '')
1256
1257 def virtual_interface_list(self, name):
1258 '''
1259 Get virtual interfaces on slice
1260 '''
1261 nt_ks = self.compute_conn
1262 nets = nt_ks.virtual_interfaces.list(self._server_uuid_from_name(name))
1263 return [network.__dict__ for network in nets]
1264
1265 def virtual_interface_create(self, name, net_name):
1266 '''
1267 Add an interfaces to a slice
1268 '''
1269 nt_ks = self.compute_conn
1270 serverid = self._server_uuid_from_name(name)
1271 networkid = self.network_show(net_name).get('id', None)
1272 if networkid is None:
1273 return {net_name: False}
1274 nets = nt_ks.virtual_interfaces.create(networkid, serverid)
1275 return nets
1276
1277 def floating_ip_pool_list(self):
1278 '''
1279 List all floating IP pools
1280 .. versionadded:: 2016.3.0
1281 '''
1282 nt_ks = self.compute_conn
1283 pools = nt_ks.floating_ip_pools.list()
1284 response = {}
1285 for pool in pools:
1286 response[pool.name] = {
1287 'name': pool.name,
1288 }
1289 return response
1290
1291 def floating_ip_list(self):
1292 '''
1293 List floating IPs
1294 .. versionadded:: 2016.3.0
1295 '''
1296 nt_ks = self.compute_conn
1297 floating_ips = nt_ks.floating_ips.list()
1298 response = {}
1299 for floating_ip in floating_ips:
1300 response[floating_ip.ip] = {
1301 'ip': floating_ip.ip,
1302 'fixed_ip': floating_ip.fixed_ip,
1303 'id': floating_ip.id,
1304 'instance_id': floating_ip.instance_id,
1305 'pool': floating_ip.pool
1306 }
1307 return response
1308
1309 def floating_ip_show(self, ip):
1310 '''
1311 Show info on specific floating IP
1312 .. versionadded:: 2016.3.0
1313 '''
1314 nt_ks = self.compute_conn
1315 floating_ips = nt_ks.floating_ips.list()
1316 for floating_ip in floating_ips:
1317 if floating_ip.ip == ip:
1318 return floating_ip
1319 return {}
1320
1321 def floating_ip_create(self, pool=None):
1322 '''
1323 Allocate a floating IP
1324 .. versionadded:: 2016.3.0
1325 '''
1326 nt_ks = self.compute_conn
1327 floating_ip = nt_ks.floating_ips.create(pool)
1328 response = {
1329 'ip': floating_ip.ip,
1330 'fixed_ip': floating_ip.fixed_ip,
1331 'id': floating_ip.id,
1332 'instance_id': floating_ip.instance_id,
1333 'pool': floating_ip.pool
1334 }
1335 return response
1336
1337 def floating_ip_delete(self, floating_ip):
1338 '''
1339 De-allocate a floating IP
1340 .. versionadded:: 2016.3.0
1341 '''
1342 ip = self.floating_ip_show(floating_ip)
1343 nt_ks = self.compute_conn
1344 return nt_ks.floating_ips.delete(ip)
1345
1346 def floating_ip_associate(self, server_name, floating_ip):
1347 '''
1348 Associate floating IP address to server
1349 .. versionadded:: 2016.3.0
1350 '''
1351 nt_ks = self.compute_conn
1352 server_ = self.server_by_name(server_name)
1353 server = nt_ks.servers.get(server_.__dict__['id'])
1354 server.add_floating_ip(floating_ip)
1355 return self.floating_ip_list()[floating_ip]
1356
1357 def floating_ip_disassociate(self, server_name, floating_ip):
1358 '''
1359 Disassociate a floating IP from server
1360 .. versionadded:: 2016.3.0
1361 '''
1362 nt_ks = self.compute_conn
1363 server_ = self.server_by_name(server_name)
1364 server = nt_ks.servers.get(server_.__dict__['id'])
1365 server.remove_floating_ip(floating_ip)
1366 return self.floating_ip_list()[floating_ip]
1367
1368#
1369# Moved from salt.modules.nova until this works in upstream
1370#
1371
1372def _auth(profile=None):
1373 '''
1374 Set up nova credentials
1375 '''
1376 if profile:
1377 credentials = __salt__['config.option'](profile)
1378 user = credentials['keystone.user']
1379 password = credentials['keystone.password']
1380 tenant = credentials['keystone.tenant']
1381 auth_url = credentials['keystone.auth_url']
1382 region_name = credentials.get('keystone.region_name', None)
1383 api_key = credentials.get('keystone.api_key', None)
1384 os_auth_system = credentials.get('keystone.os_auth_system', None)
1385 use_keystoneauth = credentials.get('keystone.use_keystoneauth', False)
1386 verify = credentials.get('keystone.verify', False)
1387 else:
1388 user = __salt__['config.option']('keystone.user')
1389 password = __salt__['config.option']('keystone.password')
1390 tenant = __salt__['config.option']('keystone.tenant')
1391 auth_url = __salt__['config.option']('keystone.auth_url')
1392 region_name = __salt__['config.option']('keystone.region_name')
1393 api_key = __salt__['config.option']('keystone.api_key')
1394 os_auth_system = __salt__['config.option']('keystone.os_auth_system')
1395 use_keystoneauth = __salt__['config.option']('keystone.use_keystoneauth', False)
1396 verify = __salt__['config.option']('keystone.verify', True)
1397
1398 kwargs = {
1399 'username': user,
1400 'password': password,
1401 'api_key': api_key,
1402 'project_id': tenant,
1403 'auth_url': auth_url,
1404 'region_name': region_name,
1405 'os_auth_plugin': os_auth_system,
1406 'use_keystoneauth': use_keystoneauth,
Oleg Iurchenko6ba00f42018-02-21 13:45:49 +02001407 'verify': verify,
1408 'profile': profile
Adam Tenglere8afccc2017-06-27 17:57:21 +00001409 }
1410
1411 return SaltNova(**kwargs)
1412
1413
Ondrej Smolae138c5b2017-11-02 11:38:23 +01001414#def boot(name, flavor_id=0, image_id=0, profile=None, timeout=300):
1415# '''
1416# Boot (create) a new instance
1417# name
1418# Name of the new instance (must be first)
1419# flavor_id
1420# Unique integer ID for the flavor
1421# image_id
1422# Unique integer ID for the image
1423# timeout
1424# How long to wait, after creating the instance, for the provider to
1425# return information about it (default 300 seconds).
1426# .. versionadded:: 2014.1.0
1427# CLI Example:
1428# .. code-block:: bash
1429# salt '*' nova.boot myinstance flavor_id=4596 image_id=2
1430# The flavor_id and image_id are obtained from nova.flavor_list and
1431# nova.image_list
1432# .. code-block:: bash
1433# salt '*' nova.flavor_list
1434# salt '*' nova.image_list
1435# '''
1436# conn = _auth(profile)
1437# return conn.boot(name, flavor_id, image_id, timeout)
Adam Tenglere8afccc2017-06-27 17:57:21 +00001438
1439
1440def volume_list(search_opts=None, profile=None):
1441 '''
1442 List storage volumes
1443 search_opts
1444 Dictionary of search options
1445 profile
1446 Profile to use
1447 CLI Example:
1448 .. code-block:: bash
1449 salt '*' nova.volume_list \
1450 search_opts='{"display_name": "myblock"}' \
1451 profile=openstack
1452 '''
1453 conn = _auth(profile)
1454 return conn.volume_list(search_opts=search_opts)
1455
1456
1457def volume_show(name, profile=None):
1458 '''
1459 Create a block storage volume
1460 name
1461 Name of the volume
1462 profile
1463 Profile to use
1464 CLI Example:
1465 .. code-block:: bash
1466 salt '*' nova.volume_show myblock profile=openstack
1467 '''
1468 conn = _auth(profile)
1469 return conn.volume_show(name)
1470
1471
1472def volume_create(name, size=100, snapshot=None, voltype=None,
1473 profile=None):
1474 '''
1475 Create a block storage volume
1476 name
1477 Name of the new volume (must be first)
1478 size
1479 Volume size
1480 snapshot
1481 Block storage snapshot id
1482 voltype
1483 Type of storage
1484 profile
1485 Profile to build on
1486 CLI Example:
1487 .. code-block:: bash
1488 salt '*' nova.volume_create myblock size=300 profile=openstack
1489 '''
1490 conn = _auth(profile)
1491 return conn.volume_create(
1492 name,
1493 size,
1494 snapshot,
1495 voltype
1496 )
1497
1498
1499def volume_delete(name, profile=None):
1500 '''
1501 Destroy the volume
1502 name
1503 Name of the volume
1504 profile
1505 Profile to build on
1506 CLI Example:
1507 .. code-block:: bash
1508 salt '*' nova.volume_delete myblock profile=openstack
1509 '''
1510 conn = _auth(profile)
1511 return conn.volume_delete(name)
1512
1513
1514def volume_detach(name,
1515 profile=None,
1516 timeout=300):
1517 '''
1518 Attach a block storage volume
1519 name
1520 Name of the new volume to attach
1521 server_name
1522 Name of the server to detach from
1523 profile
1524 Profile to build on
1525 CLI Example:
1526 .. code-block:: bash
1527 salt '*' nova.volume_detach myblock profile=openstack
1528 '''
1529 conn = _auth(profile)
1530 return conn.volume_detach(
1531 name,
1532 timeout
1533 )
1534
1535
1536def volume_attach(name,
1537 server_name,
1538 device='/dev/xvdb',
1539 profile=None,
1540 timeout=300):
1541 '''
1542 Attach a block storage volume
1543 name
1544 Name of the new volume to attach
1545 server_name
1546 Name of the server to attach to
1547 device
1548 Name of the device on the server
1549 profile
1550 Profile to build on
1551 CLI Example:
1552 .. code-block:: bash
1553 salt '*' nova.volume_attach myblock slice.example.com profile=openstack
1554 salt '*' nova.volume_attach myblock server.example.com \
1555 device='/dev/xvdb' profile=openstack
1556 '''
1557 conn = _auth(profile)
1558 return conn.volume_attach(
1559 name,
1560 server_name,
1561 device,
1562 timeout
1563 )
1564
1565
1566def suspend(instance_id, profile=None):
1567 '''
1568 Suspend an instance
1569 instance_id
1570 ID of the instance to be suspended
1571 CLI Example:
1572 .. code-block:: bash
1573 salt '*' nova.suspend 1138
1574 '''
1575 conn = _auth(profile)
1576 return conn.suspend(instance_id)
1577
1578
1579def resume(instance_id, profile=None):
1580 '''
1581 Resume an instance
1582 instance_id
1583 ID of the instance to be resumed
1584 CLI Example:
1585 .. code-block:: bash
1586 salt '*' nova.resume 1138
1587 '''
1588 conn = _auth(profile)
1589 return conn.resume(instance_id)
1590
1591
1592def lock(instance_id, profile=None):
1593 '''
1594 Lock an instance
1595 instance_id
1596 ID of the instance to be locked
1597 CLI Example:
1598 .. code-block:: bash
1599 salt '*' nova.lock 1138
1600 '''
1601 conn = _auth(profile)
1602 return conn.lock(instance_id)
1603
1604
1605def delete(instance_id, profile=None):
1606 '''
1607 Delete an instance
1608 instance_id
1609 ID of the instance to be deleted
1610 CLI Example:
1611 .. code-block:: bash
1612 salt '*' nova.delete 1138
1613 '''
1614 conn = _auth(profile)
1615 return conn.delete(instance_id)
1616
1617
1618def flavor_list(profile=None):
1619 '''
1620 Return a list of available flavors (nova flavor-list)
1621 CLI Example:
1622 .. code-block:: bash
1623 salt '*' nova.flavor_list
1624 '''
1625 conn = _auth(profile)
1626 return conn.flavor_list()
1627
1628
1629def flavor_create(name, # pylint: disable=C0103
1630 flavor_id=0, # pylint: disable=C0103
1631 ram=0,
1632 disk=0,
1633 vcpus=1,
1634 profile=None):
1635 '''
1636 Add a flavor to nova (nova flavor-create). The following parameters are
1637 required:
1638 name
1639 Name of the new flavor (must be first)
1640 flavor_id
1641 Unique integer ID for the new flavor
1642 ram
1643 Memory size in MB
1644 disk
1645 Disk size in GB
1646 vcpus
1647 Number of vcpus
1648 CLI Example:
1649 .. code-block:: bash
1650 salt '*' nova.flavor_create myflavor flavor_id=6 \
1651 ram=4096 disk=10 vcpus=1
1652 '''
1653 conn = _auth(profile)
1654 return conn.flavor_create(
1655 name,
1656 flavor_id,
1657 ram,
1658 disk,
1659 vcpus
1660 )
1661
1662
1663def flavor_delete(flavor_id, profile=None): # pylint: disable=C0103
1664 '''
1665 Delete a flavor from nova by id (nova flavor-delete)
1666 CLI Example:
1667 .. code-block:: bash
1668 salt '*' nova.flavor_delete 7
1669 '''
1670 conn = _auth(profile)
1671 return conn.flavor_delete(flavor_id)
1672
1673
1674def keypair_list(profile=None):
1675 '''
1676 Return a list of available keypairs (nova keypair-list)
1677 CLI Example:
1678 .. code-block:: bash
1679 salt '*' nova.keypair_list
1680 '''
1681 conn = _auth(profile)
1682 return conn.keypair_list()
1683
1684
1685def keypair_add(name, pubfile=None, pubkey=None, profile=None):
1686 '''
1687 Add a keypair to nova (nova keypair-add)
1688 CLI Examples:
1689 .. code-block:: bash
1690 salt '*' nova.keypair_add mykey pubfile='/home/myuser/.ssh/id_rsa.pub'
1691 salt '*' nova.keypair_add mykey pubkey='ssh-rsa <key> myuser@mybox'
1692 '''
1693 conn = _auth(profile)
1694 return conn.keypair_add(
1695 name,
1696 pubfile,
1697 pubkey
1698 )
1699
1700
1701def keypair_delete(name, profile=None):
1702 '''
1703 Add a keypair to nova (nova keypair-delete)
1704 CLI Example:
1705 .. code-block:: bash
1706 salt '*' nova.keypair_delete mykey'
1707 '''
1708 conn = _auth(profile)
1709 return conn.keypair_delete(name)
1710
1711
1712def image_list(name=None, profile=None):
1713 '''
1714 Return a list of available images (nova images-list + nova image-show)
1715 If a name is provided, only that image will be displayed.
1716 CLI Examples:
1717 .. code-block:: bash
1718 salt '*' nova.image_list
1719 salt '*' nova.image_list myimage
1720 '''
1721 conn = _auth(profile)
1722 return conn.image_list(name)
1723
1724
1725def image_meta_set(image_id=None,
1726 name=None,
1727 profile=None,
1728 **kwargs): # pylint: disable=C0103
1729 '''
1730 Sets a key=value pair in the metadata for an image (nova image-meta set)
1731 CLI Examples:
1732 .. code-block:: bash
1733 salt '*' nova.image_meta_set 6f52b2ff-0b31-4d84-8fd1-af45b84824f6 \
1734 cheese=gruyere
1735 salt '*' nova.image_meta_set name=myimage salad=pasta beans=baked
1736 '''
1737 conn = _auth(profile)
1738 return conn.image_meta_set(
1739 image_id,
1740 name,
1741 **kwargs
1742 )
1743
1744
1745def image_meta_delete(image_id=None, # pylint: disable=C0103
1746 name=None,
1747 keys=None,
1748 profile=None):
1749 '''
1750 Delete a key=value pair from the metadata for an image
1751 (nova image-meta set)
1752 CLI Examples:
1753 .. code-block:: bash
1754 salt '*' nova.image_meta_delete \
1755 6f52b2ff-0b31-4d84-8fd1-af45b84824f6 keys=cheese
1756 salt '*' nova.image_meta_delete name=myimage keys=salad,beans
1757 '''
1758 conn = _auth(profile)
1759 return conn.image_meta_delete(
1760 image_id,
1761 name,
1762 keys
1763 )
1764
1765
1766def list_(profile=None):
1767 '''
1768 To maintain the feel of the nova command line, this function simply calls
1769 the server_list function.
1770 CLI Example:
1771 .. code-block:: bash
1772 salt '*' nova.list
1773 '''
1774 return server_list(profile=profile)
1775
1776
Ondrej Smolae138c5b2017-11-02 11:38:23 +01001777#def server_list(profile=None):
1778# '''
1779# Return list of active servers
1780# CLI Example:
1781# .. code-block:: bash
1782# salt '*' nova.server_list
1783# '''
1784# conn = _auth(profile)
1785# return conn.server_list()
Adam Tenglere8afccc2017-06-27 17:57:21 +00001786
1787
1788def show(server_id, profile=None):
1789 '''
1790 To maintain the feel of the nova command line, this function simply calls
1791 the server_show function.
1792 CLI Example:
1793 .. code-block:: bash
1794 salt '*' nova.show
1795 '''
1796 return server_show(server_id, profile)
1797
1798
1799def server_list_detailed(profile=None):
1800 '''
1801 Return detailed list of active servers
1802 CLI Example:
1803 .. code-block:: bash
1804 salt '*' nova.server_list_detailed
1805 '''
1806 conn = _auth(profile)
1807 return conn.server_list_detailed()
1808
1809
1810def server_show(server_id, profile=None):
1811 '''
1812 Return detailed information for an active server
1813 CLI Example:
1814 .. code-block:: bash
1815 salt '*' nova.server_show <server_id>
1816 '''
1817 conn = _auth(profile)
1818 return conn.server_show(server_id)
1819
1820
Ondrej Smolae138c5b2017-11-02 11:38:23 +01001821#def secgroup_create(name, description, profile=None):
1822# '''
1823# Add a secgroup to nova (nova secgroup-create)
1824# CLI Example:
1825# .. code-block:: bash
1826# salt '*' nova.secgroup_create mygroup 'This is my security group'
1827# '''
1828# conn = _auth(profile)
1829# return conn.secgroup_create(name, description)
1830#
1831#
1832#def secgroup_delete(name, profile=None):
1833# '''
1834# Delete a secgroup to nova (nova secgroup-delete)
1835# CLI Example:
1836# .. code-block:: bash
1837# salt '*' nova.secgroup_delete mygroup
1838# '''
1839# conn = _auth(profile)
1840# return conn.secgroup_delete(name)
1841#
1842#
1843#def secgroup_list(profile=None):
1844# '''
1845# Return a list of available security groups (nova items-list)
1846# CLI Example:
1847# .. code-block:: bash
1848# salt '*' nova.secgroup_list
1849# '''
1850# conn = _auth(profile)
1851# return conn.secgroup_list()
Adam Tenglere8afccc2017-06-27 17:57:21 +00001852
1853
1854def server_by_name(name, profile=None):
1855 '''
1856 Return information about a server
1857 name
1858 Server Name
1859 CLI Example:
1860 .. code-block:: bash
1861 salt '*' nova.server_by_name myserver profile=openstack
1862 '''
1863 conn = _auth(profile)
1864 return conn.server_by_name(name)
1865