blob: 2d321adf7b798f920ec1a7ad1c0a7aa3b0c712da [file] [log] [blame]
Jiri Broulik0ce9fc92017-02-01 23:10:40 +01001# -*- coding: utf-8 -*-
2'''
Jiri Broulik5589c012017-06-20 11:28:52 +02003Custom Nova state
Jiri Broulik0ce9fc92017-02-01 23:10:40 +01004'''
Jiri Broulika2c79292017-02-05 21:01:38 +01005import logging
Richard Felkl55d1f572017-02-15 16:41:53 +01006import collections
Jiri Broulika2c79292017-02-05 21:01:38 +01007from functools import wraps
8LOG = logging.getLogger(__name__)
Jiri Broulik0ce9fc92017-02-01 23:10:40 +01009
10
11def __virtual__():
12 '''
13 Only load if the nova module is in __salt__
14 '''
Jiri Broulik5589c012017-06-20 11:28:52 +020015 return 'novang'
Jiri Broulik0ce9fc92017-02-01 23:10:40 +010016
Jiri Broulik70d9e3f2017-02-15 18:37:13 +010017
Jiri Broulik0ce9fc92017-02-01 23:10:40 +010018def flavor_present(name, flavor_id=0, ram=0, disk=0, vcpus=1, profile=None):
19 '''
Jiri Broulik70d9e3f2017-02-15 18:37:13 +010020 Ensures that the nova flavor exists
Jiri Broulik0ce9fc92017-02-01 23:10:40 +010021 '''
Jiri Broulik0ce9fc92017-02-01 23:10:40 +010022 ret = {'name': name,
23 'changes': {},
24 'result': True,
25 'comment': 'Flavor "{0}" already exists'.format(name)}
Adam Tenglere8afccc2017-06-27 17:57:21 +000026 project = __salt__['novang.flavor_list'](profile)
Jiri Broulik0ce9fc92017-02-01 23:10:40 +010027 if 'Error' in project:
28 pass
29 elif name in project:
30 pass
31 else:
Adam Tenglere8afccc2017-06-27 17:57:21 +000032 __salt__['novang.flavor_create'](name, flavor_id, ram, disk, vcpus, profile)
Jiri Broulik0ce9fc92017-02-01 23:10:40 +010033 ret['comment'] = 'Flavor {0} has been created'.format(name)
34 ret['changes']['Flavor'] = 'Created'
35 return ret
36
Jiri Broulik70d9e3f2017-02-15 18:37:13 +010037
Jiri Broulik5589c012017-06-20 11:28:52 +020038def map_instances(name='cell1'):
39 '''
40 Ensures that the nova instances are mapped to cell
41 '''
42 ret = {'name': name,
43 'changes': {},
44 'result': False,
45 'comment': 'Cell "{0}" does not exists'.format(name)}
Michael Polenchuked084462018-06-04 17:04:13 +040046 cell_uuid = __salt__['cmd.shell']('nova-manage cell_v2 list_cells 2>/dev/null | awk \'/' + name + '/ {print $4}\'')
Jiri Broulik7e72aa22017-07-03 16:05:11 +020047 if cell_uuid:
Jiri Broulik5589c012017-06-20 11:28:52 +020048 try:
49 __salt__['cmd.shell']('nova-manage cell_v2 map_instances --cell_uuid ' + cell_uuid)
50 ret['result'] = True
51 ret['comment'] = 'Instances were mapped to cell named {0}'.format(name)
52 ret['changes']['Instances'] = 'Mapped to cell named {0}'.format(name)
53 except:
54 ret['result'] = False
55 ret['comment'] = 'Error while mapping instances to cell named {0}'.format(name)
56 ret['changes']['Instances'] = 'Failed to map to cell named {0}'.format(name)
57 return ret
58
59
Vladimir Khlyunevd24501f2021-09-08 14:15:14 +040060def update_cell(name='cell1', transport_url='none:///', db_engine='mysql', db_name='nova_upgrade', db_user='nova', db_password=None, db_address='0.0.0.0', db_port='3306'):
Jiri Broulik789179a2018-02-13 16:16:46 +010061 '''
62 Ensures that the nova cell is setup correctly
63 '''
64 ret = {'name': name,
65 'changes': {},
66 'result': False,
67 'comment': 'Cell "{0}" does not exists'.format(name)}
Roman Lubianyi7a325992021-01-27 10:15:00 +020068 cell_uuid, ssl_enable = (__salt__['cmd.shell']('nova-manage cell_v2 list_cells 2>/dev/null | awk \'/' + name + '/ {print $4, $8}\'')).split(' ', 1)
69 if 'ssl' in ssl_enable:
70 db_options = '?charset=utf8&ssl_ca=/etc/nova/ssl/mysql/ca-cert.pem&ssl_cert=/etc/nova/ssl/mysql/client-cert.pem&ssl_key=/etc/nova/ssl/mysql/client-key.pem'
71 else:
72 db_options = '?charset=utf8'
Jiri Broulik789179a2018-02-13 16:16:46 +010073 if cell_uuid:
74 try:
Vladimir Khlyunevd24501f2021-09-08 14:15:14 +040075 __salt__['cmd.shell'](
76 'nova-manage cell_v2 update_cell '
77 '--cell_uuid {cell_uuid} '
78 '--transport-url {transport_url} '
79 '--database_connection "{db_engine}+pymysql://{db_user}:{db_password}@{db_address}:{db_port}/{db_name}{db_options}"'.format(
80 cell_uuid=cell_uuid,
81 transport_url=transport_url,
82 db_engine=db_engine,
83 db_user=db_user,
84 db_password=db_password,
85 db_address=db_address,
86 db_port=db_port,
87 db_name=db_name,
88 db_options=db_options
89 ))
Jiri Broulik789179a2018-02-13 16:16:46 +010090 ret['result'] = True
91 ret['comment'] = 'Cell {0} updated'.format(name)
92 ret['changes'][name] = 'Cell {0} successfuly updated'.format(name)
93 except:
94 ret['result'] = False
95 ret['comment'] = 'Cell {0} not updated'.format(name)
96 ret['changes'][name] = 'Cell {0} failed to be updated'.format(name)
97 return ret
98
99
Jiri Broulik91104db2017-07-07 08:50:44 +0200100def api_db_version_present(name=None, version="20"):
101 '''
102 Ensures that specific api_db version is present
103 '''
104 ret = {'name': 'api_db --version',
105 'changes': {},
106 'result': True,
107 'comment': 'Current Api_db version is not < than "{0}".'.format(version)}
108 api_db_version = __salt__['cmd.shell']('nova-manage api_db version 2>/dev/null')
109 try:
110 api_db_version = int(api_db_version)
Jiri Broulik37189a72017-07-11 16:18:55 +0200111 version = int(version)
Jiri Broulik91104db2017-07-07 08:50:44 +0200112 except:
113 # nova is not installed
114 ret = _no_change('api_db --version', None, test=True)
115 return ret
116 if api_db_version < version:
117 try:
Jiri Broulik37189a72017-07-11 16:18:55 +0200118 __salt__['cmd.shell']('nova-manage api_db sync --version ' + str(version))
Jiri Broulik91104db2017-07-07 08:50:44 +0200119 ret['result'] = True
120 ret['comment'] = 'Nova-manage api_db sync --version {0} was successfuly executed'.format(version)
121 ret['changes']['api_db'] = 'api_db sync --version {0}'.format(version)
122 except:
123 ret['result'] = False
124 ret['comment'] = 'Error while executing nova-manage api_db sync --version {0}'.format(version)
125 ret['changes']['api_db'] = 'Failed to execute api_db sync --version {0}'.format(version)
126 return ret
127
128
129def db_version_present(name=None, version="334"):
130 '''
131 Ensures that specific api_db version is present
132 '''
133 ret = {'name': 'db --version',
134 'changes': {},
135 'result': True,
136 'comment': 'Current db version is not < than "{0}".'.format(version)}
137 db_version = __salt__['cmd.shell']('nova-manage db version 2>/dev/null')
138 try:
139 db_version = int(db_version)
Jiri Broulik37189a72017-07-11 16:18:55 +0200140 version = int(version)
Jiri Broulik91104db2017-07-07 08:50:44 +0200141 except:
142 # nova is not installed
143 ret = _no_change('db --version', None, test=True)
144 return ret
145
146 if db_version < version:
147 try:
Jiri Broulik37189a72017-07-11 16:18:55 +0200148 __salt__['cmd.shell']('nova-manage db sync --version ' + str(version))
Jiri Broulik91104db2017-07-07 08:50:44 +0200149 ret['result'] = True
150 ret['comment'] = 'Nova-manage db sync --version {0} was successfuly executed'.format(version)
151 ret['changes']['db'] = 'db sync --version {0}'.format(version)
152 except:
153 ret['result'] = False
154 ret['comment'] = 'Error while executing nova-manage db sync --version {0}'.format(version)
155 ret['changes']['db'] = 'Failed to execute db sync --version {0}'.format(version)
156 return ret
157
Jiri Broulik37189a72017-07-11 16:18:55 +0200158def online_data_migrations_present(name=None, api_db_version="20", db_version="334"):
159 '''
160 Ensures that online_data_migrations are enforced if specific version of api_db and db is present
161 '''
162 ret = {'name': 'online_data_migrations',
163 'changes': {},
164 'result': True,
165 'comment': 'Current api_db version != {0} a db version != {1}.'.format(api_db_version, db_version)}
166 cur_api_db_version = __salt__['cmd.shell']('nova-manage api_db version 2>/dev/null')
167 cur_db_version = __salt__['cmd.shell']('nova-manage db version 2>/dev/null')
168 try:
169 cur_api_db_version = int(cur_api_db_version)
170 cur_db_version = int(cur_db_version)
171 api_db_version = int(api_db_version)
172 db_version = int(db_version)
173 except:
174 # nova is not installed
175 ret = _no_change('online_data_migrations', None, test=True)
176 return ret
177 if cur_api_db_version == api_db_version and cur_db_version == db_version:
178 try:
179 __salt__['cmd.shell']('nova-manage db online_data_migrations')
180 ret['result'] = True
181 ret['comment'] = 'nova-manage db online_data_migrations was successfuly executed'
182 ret['changes']['online_data_migrations'] = 'online_data_migrations on api_db version {0} and db version {1}'.format(api_db_version, db_version)
183 except:
184 ret['result'] = False
185 ret['comment'] = 'Error while executing nova-manage db online_data_migrations'
186 ret['changes']['online_data_migrations'] = 'Failed to execute online_data_migrations on api_db version {0} and db version {1}'.format(api_db_version, db_version)
187 return ret
188
Jiri Broulika2c79292017-02-05 21:01:38 +0100189def quota_present(tenant_name, profile, name=None, **kwargs):
190 '''
191 Ensures that the nova quota exists
192 '''
193 changes = {}
194 for key, value in kwargs.items():
195 quota = __salt__['novang.quota_get'](key, tenant_name, profile)
196 if quota != value:
197 arg = {}
198 arg[key] = value
199 changes[key] = value
200 __salt__['novang.quota_update'](tenant_name, profile, **arg)
201 if bool(changes):
Jiri Broulik70d9e3f2017-02-15 18:37:13 +0100202 return _updated(tenant_name, 'tenant', changes)
Jiri Broulika2c79292017-02-05 21:01:38 +0100203 else:
204 return _no_change(tenant_name, 'tenant')
205
Jiri Broulika2c79292017-02-05 21:01:38 +0100206
Jiri Broulik70d9e3f2017-02-15 18:37:13 +0100207def availability_zone_present(name=None, availability_zone=None, profile=None):
208 '''
209 Ensures that the nova availability zone exists
210 '''
211 name = availability_zone
212 zone_exists = __salt__['novang.availability_zone_get'](name, profile)
213 if zone_exists == False:
214 item_created = __salt__['novang.availability_zone_create'](name, availability_zone, profile)
215 if bool(item_created):
Michael Polenchuked084462018-06-04 17:04:13 +0400216 return _created(availability_zone, 'availabilty zone', item_created)
Jiri Broulika2c79292017-02-05 21:01:38 +0100217 else:
Jiri Broulik70d9e3f2017-02-15 18:37:13 +0100218 return _already_exists(availability_zone, 'availabilty zone')
219 return existing_availability_zones
220
Damian Szeluga5dca0f02017-04-13 17:27:15 +0200221def aggregate_present(name=None, aggregate=None, profile=None):
222 '''
223 Ensures that the nova aggregate exists
224 '''
225 name = aggregate
226 aggregate_exists = __salt__['novang.aggregate_get'](name, profile)
227 if aggregate_exists == False:
228 item_created = __salt__['novang.aggregate_create'](name, aggregate, profile)
229 if bool(item_created):
230 return _created(aggregate, 'aggregate', item_created)
231 else:
232 return _already_exists(aggregate, 'aggregate')
233 return existing_aggregate
234
Richard Felkl55d1f572017-02-15 16:41:53 +0100235
236def instance_present(name, flavor, image, networks, security_groups=None, profile=None, tenant_name=None):
237 ret = {'name': name,
238 'changes': {},
239 'result': True,
240 'comment': 'Instance "{0}" already exists'.format(name)}
241 kwargs = {}
242 nics = []
243 existing_instances = __salt__['novang.server_list'](profile, tenant_name)
244 if name in existing_instances:
245 return ret
Adam Tenglere8afccc2017-06-27 17:57:21 +0000246 existing_flavors = __salt__['novang.flavor_list'](profile)
Richard Felkl55d1f572017-02-15 16:41:53 +0100247 if flavor in existing_flavors:
Ondrej Smolab7b0dda2017-02-28 14:36:46 +0100248 flavor_id = existing_flavors[flavor]['id']
Richard Felkl55d1f572017-02-15 16:41:53 +0100249 else:
250 return {'name': name,
251 'changes': {},
252 'result': False,
253 'comment': 'Flavor "{0}" doesn\'t exists'.format(flavor)}
254
Adam Tenglere8afccc2017-06-27 17:57:21 +0000255 existing_image = __salt__['novang.image_list'](image, profile)
Richard Felkl55d1f572017-02-15 16:41:53 +0100256 if not existing_image:
257 return {'name': name,
258 'changes': {},
259 'result': False,
260 'comment': 'Image "{0}" doesn\'t exists'.format(image)}
261 else:
262 image_id = existing_image.get(image).get('id')
263 if security_groups is not None:
264 kwargs['security_groups'] = []
265 for secgroup in security_groups:
266 existing_secgroups = __salt__['novang.secgroup_list'](profile, tenant_name)
267 if not secgroup in existing_secgroups:
268 return {'name': name,
269 'changes': {},
270 'result': False,
271 'comment': 'Security group "{0}" doesn\'t exists'.format(secgroup)}
272 else:
273 kwargs['security_groups'].append(secgroup)
274 for net in networks:
275 existing_network = __salt__['novang.network_show'](net.get('name'), profile)
276 if not existing_network:
277 return {'name': name,
278 'changes': {},
279 'result': False,
280 'comment': 'Network "{0}" doesn\'t exists'.format(net.get(name))}
281 else:
282 network_id = existing_network.get('id')
283 if net.get('v4_fixed_ip') is not None:
284 nics.append({'net-id': network_id, 'v4-fixed-ip': net.get('v4_fixed_ip')})
285 else:
286 nics.append({'net-id': network_id})
287 kwargs['nics'] = nics
288 new_instance_id = __salt__['novang.boot'] (name, flavor_id, image_id, profile, tenant_name, **kwargs)
289 return {'name': name,
290 'changes': {},
291 'result': True,
292 'comment': 'Instance "{0}" was successfuly created'.format(name)}
Jiri Broulik70d9e3f2017-02-15 18:37:13 +0100293
Elena Ezhovac1bcbfa2017-07-13 16:13:43 +0400294
Elena Ezhova9b7a1082017-08-02 17:40:46 +0400295def keypair_present(name, pub_file=None, pub_key=None, profile=None):
Elena Ezhovac1bcbfa2017-07-13 16:13:43 +0400296 """
297 Ensures that the Nova key-pair exists
298 """
299
300 existing_keypairs = __salt__['novang.keypair_list'](profile)
301 if name in existing_keypairs:
302 return _already_exists(name, 'Keypair')
303 else:
304 res = __salt__['novang.keypair_add'](name, pubfile=pub_file,
Elena Ezhova9b7a1082017-08-02 17:40:46 +0400305 pubkey=pub_key, profile=profile)
Elena Ezhovac1bcbfa2017-07-13 16:13:43 +0400306 if res and res['name'] == name:
307 return _created(name, 'Keypair', res)
308 return _create_failed(name, 'Keypair')
309
310
Jiri Broulik70d9e3f2017-02-15 18:37:13 +0100311def _already_exists(name, resource):
312 changes_dict = {'name': name,
313 'changes': {},
314 'result': True}
315 changes_dict['comment'] = \
316 '{0} {1} already exists'.format(resource, name)
317 return changes_dict
318
319
320def _created(name, resource, resource_definition):
321 changes_dict = {'name': name,
322 'changes': resource_definition,
323 'result': True,
324 'comment': '{0} {1} created'.format(resource, name)}
325 return changes_dict
326
327def _updated(name, resource, resource_definition):
328 changes_dict = {'name': name,
329 'changes': resource_definition,
330 'result': True,
331 'comment': '{0} {1} tenant was updated'.format(resource, name)}
332 return changes_dict
333
334def _update_failed(name, resource):
335 changes_dict = {'name': name,
336 'changes': {},
337 'comment': '{0} {1} failed to update'.format(resource, name),
338 'result': False}
339 return changes_dict
340
341def _no_change(name, resource, test=False):
342 changes_dict = {'name': name,
343 'changes': {},
344 'result': True}
345 if test:
346 changes_dict['comment'] = \
347 '{0} {1} will be {2}'.format(resource, name, test)
348 else:
349 changes_dict['comment'] = \
350 '{0} {1} is in correct state'.format(resource, name)
Damian Szeluga5dca0f02017-04-13 17:27:15 +0200351 return changes_dict
Adam Tenglere8afccc2017-06-27 17:57:21 +0000352
Elena Ezhovac1bcbfa2017-07-13 16:13:43 +0400353
354def _create_failed(name, resource):
355 changes_dict = {'name': name,
356 'changes': {},
357 'comment': '{0} {1} failed to create'.format(resource,
358 name),
359 'result': False}
360 return changes_dict