blob: 7bfa7fb25b14cae0c9edc939bfff7512042832b9 [file] [log] [blame]
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +03001import logging
Vasyl Saienkoba420732018-09-07 10:19:32 +00002import random
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +03003
4log = logging.getLogger(__name__)
5
6
7def __virtual__():
8 return 'neutronv2' if 'neutronv2.subnet_list' in __salt__ else False
9
10
11def _neutronv2_call(fname, *args, **kwargs):
12 return __salt__['neutronv2.{}'.format(fname)](*args, **kwargs)
13
14
Ann Taraday8204f722018-12-12 16:38:57 +040015def _try_get_resource(resource, name, cloud_name):
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +030016 try:
17 method_name = '{}_get_details'.format(resource)
18 exact_resource = _neutronv2_call(
Oleksiy Petrenko5bfb8bc2018-08-23 15:08:17 +030019 method_name, name, cloud_name=cloud_name
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +030020 )[resource]
21 except Exception as e:
22 if 'ResourceNotFound' in repr(e):
Ann Taraday8204f722018-12-12 16:38:57 +040023 return None
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +030024 else:
25 raise
Ann Taraday8204f722018-12-12 16:38:57 +040026 return exact_resource
27
28def _resource_present(resource, resource_name, changeable_params, cloud_name,
29 **kwargs):
30 exact_resource = None
31 try:
32 exact_resource = _try_get_resource(resource, resource_name, cloud_name)
33 if exact_resource is None:
34 # in case of rename - check if resource was already renamed
35 if kwargs.get('name') is not None and kwargs.get(
36 'name') != resource_name:
37 exact_resource = _try_get_resource(resource,
38 kwargs.get('name'),
39 cloud_name)
40 except Exception as e:
41 if 'MultipleResourcesFound' in repr(e):
42 return _failed('find', resource_name, resource)
43 else:
44 raise
45 if exact_resource is None:
46 try:
47 method_name = '{}_create'.format(resource)
48 exact_resource_name = kwargs.pop('name', resource_name)
49 resp = _neutronv2_call(
50 method_name, name=exact_resource_name,
51 cloud_name=cloud_name, **kwargs)
52 except Exception as e:
53 log.exception('Neutron {0} create failed with {1}'.
54 format(resource, e))
55 return _failed('create', exact_resource_name, resource)
56 return _succeeded('create', exact_resource_name, resource, resp)
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +030057
58 to_update = {}
59 for key in kwargs:
60 if key in changeable_params and (key not in exact_resource
61 or kwargs[key] != exact_resource[key]):
62 to_update[key] = kwargs[key]
Ann Taraday8204f722018-12-12 16:38:57 +040063 if to_update:
64 try:
65 method_name = '{}_update'.format(resource)
66 resp = _neutronv2_call(
67 method_name, resource_name, cloud_name=cloud_name, **to_update
68 )
69 except Exception as e:
70 log.exception('Neutron {0} update failed with {1}'.format(resource, e))
71 return _failed('update', resource_name, resource)
72 return _succeeded('update', resource_name, resource, resp)
73 else:
74 return _succeeded('no_changes', resource_name, resource)
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +030075
76
77def _resource_absent(resource, name, cloud_name):
78 try:
79 method_name = '{}_get_details'.format(resource)
80 _neutronv2_call(
Oleksiy Petrenko5bfb8bc2018-08-23 15:08:17 +030081 method_name, name, cloud_name=cloud_name
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +030082 )[resource]
83 except Exception as e:
84 if 'ResourceNotFound' in repr(e):
85 return _succeeded('absent', name, resource)
86 if 'MultipleResourcesFound' in repr(e):
87 return _failed('find', name, resource)
88 try:
89 method_name = '{}_delete'.format(resource)
90 _neutronv2_call(
Oleksiy Petrenko5bfb8bc2018-08-23 15:08:17 +030091 method_name, name, cloud_name=cloud_name
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +030092 )
93 except Exception as e:
94 log.error('Neutron delete {0} failed with {1}'.format(resource, e))
95 return _failed('delete', name, resource)
96 return _succeeded('delete', name, resource)
97
98
99def network_present(name, cloud_name, **kwargs):
100 changeable = (
101 'admin_state_up', 'dns_domain', 'mtu', 'port_security_enabled',
102 'provider:network_type', 'provider:physical_network',
103 'provider:segmentation_id', 'qos_policy_id', 'router:external',
104 'segments', 'shared', 'description', 'is_default'
105 )
106
107 return _resource_present('network', name, changeable, cloud_name, **kwargs)
108
109
110def network_absent(name, cloud_name):
111 return _resource_absent('network', name, cloud_name)
112
113
114def subnet_present(name, cloud_name, network_id, ip_version, cidr, **kwargs):
115 kwargs.update({'network_id': network_id,
116 'ip_version': ip_version,
117 'cidr': cidr})
118 changeable = (
119 'name', 'enable_dhcp', 'dns_nameservers', 'allocation_pools',
120 'host_routes', 'gateway_ip', 'description', 'service_types',
121 )
122
123 return _resource_present('subnet', name, changeable, cloud_name, **kwargs)
124
125
126def subnet_absent(name, cloud_name):
127 return _resource_absent('subnet', name, cloud_name)
128
129
130def subnetpool_present(name, cloud_name, prefixes, **kwargs):
131 kwargs.update({'prefixes': prefixes})
132 changeable = (
133 'default_quota', 'min_prefixlen', 'address_scope_id',
134 'default_prefixlen', 'description'
135 )
136
137 return _resource_present('subnetpool', name, changeable, cloud_name, **kwargs)
138
139
140def subnetpool_absent(name, cloud_name):
141 return _resource_absent('subnetpool', name, cloud_name)
142
143
Oleksiy Petrenko5bfb8bc2018-08-23 15:08:17 +0300144def agent_present(name, agent_type, cloud_name, **kwargs):
145 """
146 :param name: agent host name
147 :param agent_type: type of the agent. i.e. 'L3 agent' or 'DHCP agent'
148 :param kwargs:
149 :param description: agent description
150 :param admin_state_up: administrative state of the agent
151 """
152 agents = _neutronv2_call(
153 'agent_list', host=name, agent_type=agent_type,
154 cloud_name=cloud_name)['agents']
155 # Make sure we have one and only one such agent
156 if len(agents) == 1:
157 agent = agents[0]
158 to_update = {}
159 for key in kwargs:
160 if kwargs[key] != agent[key]:
161 to_update[key] = kwargs[key]
162 if to_update:
163 try:
164 _neutronv2_call('agent_update', agent_id=agent['id'],
165 cloud_name=cloud_name, **kwargs)
166 except Exception:
167 return _failed('update', name, 'agent')
168 return _succeeded('update', name, 'agent')
169 return _succeeded('no_changes', name, 'agent')
170 else:
171 return _failed('find', name, 'agent')
172
173
Vasyl Saienko2893de32018-08-15 13:39:17 +0000174def agents_disabled(name, cloud_name, **kwargs):
175 """
176 :param name: agent host name
177 :param kwargs:
178 :param description: agent description
179 :param admin_state_up: administrative state of the agent
180 """
181 agents = _neutronv2_call(
182 'agent_list', host=name, cloud_name=cloud_name)['agents']
183
184 changes = {}
185 for agent in agents:
186 if agent['admin_state_up'] == True:
187 try:
188 changes[agent['id']] = _neutronv2_call('agent_update', agent_id=agent['id'],
189 cloud_name=cloud_name, admin_state_up=False)
190 except Exception:
191 return _failed('update', name, 'agent')
192 return _succeeded('update', name, 'agent',changes)
193
194
195def agents_enabled(name, cloud_name, **kwargs):
196 """
197 :param name: agent host name
198 :param kwargs:
199 :param description: agent description
200 :param admin_state_up: administrative state of the agent
201 """
202 agents = _neutronv2_call(
203 'agent_list', host=name, cloud_name=cloud_name)['agents']
204
205 changes = {}
206 for agent in agents:
207 if agent['admin_state_up'] == False:
208 try:
209 changes[agent['id']] = _neutronv2_call('agent_update', agent_id=agent['id'],
210 cloud_name=cloud_name, admin_state_up=True)
211 except Exception:
212 return _failed('update', name, 'agent')
213
214 return _succeeded('update', name, 'agent', changes)
215
216
Vasyl Saienkoba420732018-09-07 10:19:32 +0000217def l3_resources_moved(name, cloud_name, target=None):
218 """
219 Ensure l3 resources are moved to target/other nodes
220 Move non-HA (legacy and DVR) routers.
221
222 :param name: agent host to remove routers from
223 :param target: target host to move routers to
224 :param cloud_name: name of cloud from os client config
225 """
226
227 all_agents = _neutronv2_call(
228 'agent_list', agent_type='L3 agent', cloud_name=cloud_name)['agents']
229
230 current_agent_id = [x['id'] for x in all_agents if x['host'] == name][0]
231
232 if target is not None:
233 target_agents = [x['id'] for x in all_agents if x['host'] == target]
234 else:
235 target_agents = [x['id'] for x in all_agents
236 if x['host'] != name and x['alive'] and x['admin_state_up']]
237
238 if len(target_agents) == 0:
239 log.error("No candidate agents to move routers.")
240 return _failed('resources_moved', name, 'L3 agent')
241
242 routers_on_agent = _neutronv2_call(
243 'l3_agent_router_list', current_agent_id, cloud_name=cloud_name)['routers']
244
245 routers_on_agent = [x for x in routers_on_agent if x['ha'] == False]
246
247 try:
248 for router in routers_on_agent:
249 _neutronv2_call(
250 'l3_agent_router_remove', router_id=router['id'],
251 agent_id=current_agent_id, cloud_name=cloud_name)
252 _neutronv2_call(
253 'l3_agent_router_schedule', router_id=router['id'],
254 agent_id=random.choice(target_agents),
255 cloud_name=cloud_name)
256 except Exception as e:
257 log.exception("Failed to move router from {0}: {1}".format(name, e))
258 return _failed('resources_moved', name, 'L3 agent')
259
260 return _succeeded('resources_moved', name, 'L3 agent')
261
262
Ann Taraday8204f722018-12-12 16:38:57 +0400263def port_present(port_name, cloud_name, **kwargs):
264 changeable = (
265 'name', 'description', 'device_id', 'qos_policy',
266 'allowed_address_pair', 'fixed_ip',
267 'device_owner', 'admin_state_up', 'security_group', 'extra_dhcp_opt',
268 )
269
270 return _resource_present('port', port_name, changeable,
271 cloud_name, **kwargs)
272
273
Vyacheslav Struk3f529d42019-06-13 13:37:25 +0300274def rbac_get_rule_id(cloud_name, **kwargs):
275 existing_rules = _neutronv2_call('rbac_policies_list',
276 cloud_name=cloud_name)
277
278 match_condition_fields = ['action',
279 'target_tenant',
280 'object_id',
281 ]
282
283 for rule in existing_rules['rbac_policies']:
284 match = True
285 for field in match_condition_fields:
286 if rule[field] != kwargs[field]:
287 match = False
288 break
289 if match: return rule['id']
290
291
292def rbac_present(name, cloud_name, **kwargs):
293 resource = 'rbac_policies'
294 # Resolve network name to UID if needed
295 kwargs['object_id'] = __salt__['neutronv2.network_get_details'] \
296 (network_id=kwargs['object_id'],cloud_name=cloud_name)['network']['id']
297
298 if rbac_get_rule_id(cloud_name, **kwargs):
299 return _succeeded('no_changes', name, resource)
300
301 r = _neutronv2_call('{}_create'.format(resource),
302 cloud_name=cloud_name,
303 **kwargs)
304 if r:
305 return _succeeded('create', name, resource, changes=r)
306 else:
307 return _failed('create', name, kwargs)
308
309def rbac_absent(name, cloud_name, **kwargs):
310 resource = 'rbac_policies'
311 # Resolve network name to UID if needed
312 kwargs['object_id'] = __salt__['neutronv2.network_get_details'] \
313 (network_id=kwargs['object_id'],cloud_name=cloud_name)['network']['id']
314
315 rule_id = rbac_get_rule_id(cloud_name, **kwargs)
316
317 if rule_id:
318 r = _neutronv2_call('{}_delete'.format(resource),
319 cloud_name=cloud_name,
320 id=rule_id)
321 return _succeeded('delete', name, resource, changes=r)
322
323 return _succeeded('no_changes', name, resource)
324
325
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +0300326def _succeeded(op, name, resource, changes=None):
327 msg_map = {
328 'create': '{0} {1} created',
329 'delete': '{0} {1} removed',
330 'update': '{0} {1} updated',
331 'no_changes': '{0} {1} is in desired state',
Vasyl Saienkoba420732018-09-07 10:19:32 +0000332 'absent': '{0} {1} not present',
333 'resources_moved': '{1} resources were moved from {0}',
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +0300334 }
335 changes_dict = {
336 'name': name,
337 'result': True,
338 'comment': msg_map[op].format(resource, name),
339 'changes': changes or {},
340 }
341 return changes_dict
342
343
344def _failed(op, name, resource):
345 msg_map = {
346 'create': '{0} {1} failed to create',
347 'delete': '{0} {1} failed to delete',
348 'update': '{0} {1} failed to update',
Vasyl Saienkoba420732018-09-07 10:19:32 +0000349 'find': '{0} {1} found multiple {0}',
350 'resources_moved': 'failed to move {1} from {0}',
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +0300351 }
352 changes_dict = {
353 'name': name,
354 'result': False,
355 'comment': msg_map[op].format(resource, name),
356 'changes': {},
357 }
358 return changes_dict