blob: b4fd30dab50ead59a36d0ffab21baa2bdc5960a5 [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
15def _resource_present(resource, name, changeable_params, cloud_name, **kwargs):
16 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):
23 try:
24 method_name = '{}_create'.format(resource)
25 resp = _neutronv2_call(
26 method_name, name=name, cloud_name=cloud_name, **kwargs
27 )
28 except Exception as e:
29 log.exception('Neutron {0} create failed with {1}'.
30 format(resource, e))
31 return _failed('create', name, resource)
32 return _succeeded('create', name, resource, resp)
33 elif 'MultipleResourcesFound' in repr(e):
34 return _failed('find', name, resource)
35 else:
36 raise
37
38 to_update = {}
39 for key in kwargs:
40 if key in changeable_params and (key not in exact_resource
41 or kwargs[key] != exact_resource[key]):
42 to_update[key] = kwargs[key]
43 try:
44 method_name = '{}_update'.format(resource)
45 resp = _neutronv2_call(
Oleg Bondarevd58f4ec2018-09-17 13:46:39 +040046 method_name, name, cloud_name=cloud_name, **to_update
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +030047 )
48 except Exception as e:
49 log.exception('Neutron {0} update failed with {1}'.format(resource, e))
50 return _failed('update', name, resource)
51 return _succeeded('update', name, resource, resp)
52
53
54def _resource_absent(resource, name, cloud_name):
55 try:
56 method_name = '{}_get_details'.format(resource)
57 _neutronv2_call(
Oleksiy Petrenko5bfb8bc2018-08-23 15:08:17 +030058 method_name, name, cloud_name=cloud_name
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +030059 )[resource]
60 except Exception as e:
61 if 'ResourceNotFound' in repr(e):
62 return _succeeded('absent', name, resource)
63 if 'MultipleResourcesFound' in repr(e):
64 return _failed('find', name, resource)
65 try:
66 method_name = '{}_delete'.format(resource)
67 _neutronv2_call(
Oleksiy Petrenko5bfb8bc2018-08-23 15:08:17 +030068 method_name, name, cloud_name=cloud_name
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +030069 )
70 except Exception as e:
71 log.error('Neutron delete {0} failed with {1}'.format(resource, e))
72 return _failed('delete', name, resource)
73 return _succeeded('delete', name, resource)
74
75
76def network_present(name, cloud_name, **kwargs):
77 changeable = (
78 'admin_state_up', 'dns_domain', 'mtu', 'port_security_enabled',
79 'provider:network_type', 'provider:physical_network',
80 'provider:segmentation_id', 'qos_policy_id', 'router:external',
81 'segments', 'shared', 'description', 'is_default'
82 )
83
84 return _resource_present('network', name, changeable, cloud_name, **kwargs)
85
86
87def network_absent(name, cloud_name):
88 return _resource_absent('network', name, cloud_name)
89
90
91def subnet_present(name, cloud_name, network_id, ip_version, cidr, **kwargs):
92 kwargs.update({'network_id': network_id,
93 'ip_version': ip_version,
94 'cidr': cidr})
95 changeable = (
96 'name', 'enable_dhcp', 'dns_nameservers', 'allocation_pools',
97 'host_routes', 'gateway_ip', 'description', 'service_types',
98 )
99
100 return _resource_present('subnet', name, changeable, cloud_name, **kwargs)
101
102
103def subnet_absent(name, cloud_name):
104 return _resource_absent('subnet', name, cloud_name)
105
106
107def subnetpool_present(name, cloud_name, prefixes, **kwargs):
108 kwargs.update({'prefixes': prefixes})
109 changeable = (
110 'default_quota', 'min_prefixlen', 'address_scope_id',
111 'default_prefixlen', 'description'
112 )
113
114 return _resource_present('subnetpool', name, changeable, cloud_name, **kwargs)
115
116
117def subnetpool_absent(name, cloud_name):
118 return _resource_absent('subnetpool', name, cloud_name)
119
120
Oleksiy Petrenko5bfb8bc2018-08-23 15:08:17 +0300121def agent_present(name, agent_type, cloud_name, **kwargs):
122 """
123 :param name: agent host name
124 :param agent_type: type of the agent. i.e. 'L3 agent' or 'DHCP agent'
125 :param kwargs:
126 :param description: agent description
127 :param admin_state_up: administrative state of the agent
128 """
129 agents = _neutronv2_call(
130 'agent_list', host=name, agent_type=agent_type,
131 cloud_name=cloud_name)['agents']
132 # Make sure we have one and only one such agent
133 if len(agents) == 1:
134 agent = agents[0]
135 to_update = {}
136 for key in kwargs:
137 if kwargs[key] != agent[key]:
138 to_update[key] = kwargs[key]
139 if to_update:
140 try:
141 _neutronv2_call('agent_update', agent_id=agent['id'],
142 cloud_name=cloud_name, **kwargs)
143 except Exception:
144 return _failed('update', name, 'agent')
145 return _succeeded('update', name, 'agent')
146 return _succeeded('no_changes', name, 'agent')
147 else:
148 return _failed('find', name, 'agent')
149
150
Vasyl Saienko2893de32018-08-15 13:39:17 +0000151def agents_disabled(name, cloud_name, **kwargs):
152 """
153 :param name: agent host name
154 :param kwargs:
155 :param description: agent description
156 :param admin_state_up: administrative state of the agent
157 """
158 agents = _neutronv2_call(
159 'agent_list', host=name, cloud_name=cloud_name)['agents']
160
161 changes = {}
162 for agent in agents:
163 if agent['admin_state_up'] == True:
164 try:
165 changes[agent['id']] = _neutronv2_call('agent_update', agent_id=agent['id'],
166 cloud_name=cloud_name, admin_state_up=False)
167 except Exception:
168 return _failed('update', name, 'agent')
169 return _succeeded('update', name, 'agent',changes)
170
171
172def agents_enabled(name, cloud_name, **kwargs):
173 """
174 :param name: agent host name
175 :param kwargs:
176 :param description: agent description
177 :param admin_state_up: administrative state of the agent
178 """
179 agents = _neutronv2_call(
180 'agent_list', host=name, cloud_name=cloud_name)['agents']
181
182 changes = {}
183 for agent in agents:
184 if agent['admin_state_up'] == False:
185 try:
186 changes[agent['id']] = _neutronv2_call('agent_update', agent_id=agent['id'],
187 cloud_name=cloud_name, admin_state_up=True)
188 except Exception:
189 return _failed('update', name, 'agent')
190
191 return _succeeded('update', name, 'agent', changes)
192
193
Vasyl Saienkoba420732018-09-07 10:19:32 +0000194def l3_resources_moved(name, cloud_name, target=None):
195 """
196 Ensure l3 resources are moved to target/other nodes
197 Move non-HA (legacy and DVR) routers.
198
199 :param name: agent host to remove routers from
200 :param target: target host to move routers to
201 :param cloud_name: name of cloud from os client config
202 """
203
204 all_agents = _neutronv2_call(
205 'agent_list', agent_type='L3 agent', cloud_name=cloud_name)['agents']
206
207 current_agent_id = [x['id'] for x in all_agents if x['host'] == name][0]
208
209 if target is not None:
210 target_agents = [x['id'] for x in all_agents if x['host'] == target]
211 else:
212 target_agents = [x['id'] for x in all_agents
213 if x['host'] != name and x['alive'] and x['admin_state_up']]
214
215 if len(target_agents) == 0:
216 log.error("No candidate agents to move routers.")
217 return _failed('resources_moved', name, 'L3 agent')
218
219 routers_on_agent = _neutronv2_call(
220 'l3_agent_router_list', current_agent_id, cloud_name=cloud_name)['routers']
221
222 routers_on_agent = [x for x in routers_on_agent if x['ha'] == False]
223
224 try:
225 for router in routers_on_agent:
226 _neutronv2_call(
227 'l3_agent_router_remove', router_id=router['id'],
228 agent_id=current_agent_id, cloud_name=cloud_name)
229 _neutronv2_call(
230 'l3_agent_router_schedule', router_id=router['id'],
231 agent_id=random.choice(target_agents),
232 cloud_name=cloud_name)
233 except Exception as e:
234 log.exception("Failed to move router from {0}: {1}".format(name, e))
235 return _failed('resources_moved', name, 'L3 agent')
236
237 return _succeeded('resources_moved', name, 'L3 agent')
238
239
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +0300240def _succeeded(op, name, resource, changes=None):
241 msg_map = {
242 'create': '{0} {1} created',
243 'delete': '{0} {1} removed',
244 'update': '{0} {1} updated',
245 'no_changes': '{0} {1} is in desired state',
Vasyl Saienkoba420732018-09-07 10:19:32 +0000246 'absent': '{0} {1} not present',
247 'resources_moved': '{1} resources were moved from {0}',
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +0300248 }
249 changes_dict = {
250 'name': name,
251 'result': True,
252 'comment': msg_map[op].format(resource, name),
253 'changes': changes or {},
254 }
255 return changes_dict
256
257
258def _failed(op, name, resource):
259 msg_map = {
260 'create': '{0} {1} failed to create',
261 'delete': '{0} {1} failed to delete',
262 'update': '{0} {1} failed to update',
Vasyl Saienkoba420732018-09-07 10:19:32 +0000263 'find': '{0} {1} found multiple {0}',
264 'resources_moved': 'failed to move {1} from {0}',
Oleksiy Petrenkocaad2032018-04-20 14:42:46 +0300265 }
266 changes_dict = {
267 'name': name,
268 'result': False,
269 'comment': msg_map[op].format(resource, name),
270 'changes': {},
271 }
272 return changes_dict