1 # Licensed under the Apache License, Version 2.0 (the "License"); you may
2 # not use this file except in compliance with the License. You may obtain
3 # a copy of the License at
5 # http://www.apache.org/licenses/LICENSE-2.0
7 # Unless required by applicable law or agreed to in writing, software
8 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 # License for the specific language governing permissions and limitations
15 from oslo_serialization import jsonutils as json
16 from six.moves.urllib import parse as urlparse
17 from tempest_lib.common.utils import misc
18 from tempest_lib import exceptions as lib_exc
20 from neutron.tests.tempest.common import service_client
21 from neutron.tests.tempest import exceptions
24 class NetworkClientJSON(service_client.ServiceClient):
27 Tempest REST client for Neutron. Uses v2 of the Neutron API, since the
28 V1 API has been removed from the code base.
30 Implements create, delete, update, list and show for the basic Neutron
31 abstractions (networks, sub-networks, routers, ports and floating IP):
33 Implements add/remove interface to router using subnet ID / port ID
35 It also implements list, show, update and reset for OpenStack Networking
42 def get_uri(self, plural_name):
43 # get service prefix from resource name
45 # The following list represents resource names that do not require
46 # changing underscore to a hyphen
48 "firewall_rules", "firewall_policies", "service_profiles"]
49 # the following map is used to construct proper URI
50 # for the given neutron resource
51 service_resource_prefix_map = {
56 'ipsecpolicies': 'vpn',
59 'ipsec-site-connections': 'vpn',
60 'metering_labels': 'metering',
61 'metering_label_rules': 'metering',
62 'firewall_rules': 'fw',
63 'firewall_policies': 'fw',
66 'bandwidth_limit_rules': 'qos',
70 service_prefix = service_resource_prefix_map.get(
72 if plural_name not in hyphen_exceptions:
73 plural_name = plural_name.replace("_", "-")
75 uri = '%s/%s/%s' % (self.uri_prefix, service_prefix,
78 uri = '%s/%s' % (self.uri_prefix, plural_name)
81 def pluralize(self, resource_name):
82 # get plural from map or just add 's'
84 # map from resource name to a plural name
85 # needed only for those which can't be constructed as name + 's'
86 resource_plural_map = {
87 'security_groups': 'security_groups',
88 'security_group_rules': 'security_group_rules',
89 'ipsecpolicy': 'ipsecpolicies',
90 'ikepolicy': 'ikepolicies',
91 'ipsec_site_connection': 'ipsec-site-connections',
93 'firewall_policy': 'firewall_policies',
94 'qos_policy': 'policies',
95 'rbac_policy': 'rbac_policies',
97 return resource_plural_map.get(resource_name, resource_name + 's')
99 def _lister(self, plural_name):
100 def _list(**filters):
101 uri = self.get_uri(plural_name)
103 uri += '?' + urlparse.urlencode(filters, doseq=1)
104 resp, body = self.get(uri)
105 result = {plural_name: self.deserialize_list(body)}
106 self.expected_success(200, resp.status)
107 return service_client.ResponseBody(resp, result)
111 def _deleter(self, resource_name):
112 def _delete(resource_id):
113 plural = self.pluralize(resource_name)
114 uri = '%s/%s' % (self.get_uri(plural), resource_id)
115 resp, body = self.delete(uri)
116 self.expected_success(204, resp.status)
117 return service_client.ResponseBody(resp, body)
121 def _shower(self, resource_name):
122 def _show(resource_id, **fields):
123 # fields is a dict which key is 'fields' and value is a
124 # list of field's name. An example:
125 # {'fields': ['id', 'name']}
126 plural = self.pluralize(resource_name)
127 uri = '%s/%s' % (self.get_uri(plural), resource_id)
129 uri += '?' + urlparse.urlencode(fields, doseq=1)
130 resp, body = self.get(uri)
131 body = self.deserialize_single(body)
132 self.expected_success(200, resp.status)
133 return service_client.ResponseBody(resp, body)
137 def _creater(self, resource_name):
138 def _create(**kwargs):
139 plural = self.pluralize(resource_name)
140 uri = self.get_uri(plural)
141 post_data = self.serialize({resource_name: kwargs})
142 resp, body = self.post(uri, post_data)
143 body = self.deserialize_single(body)
144 self.expected_success(201, resp.status)
145 return service_client.ResponseBody(resp, body)
149 def _updater(self, resource_name):
150 def _update(res_id, **kwargs):
151 plural = self.pluralize(resource_name)
152 uri = '%s/%s' % (self.get_uri(plural), res_id)
153 post_data = self.serialize({resource_name: kwargs})
154 resp, body = self.put(uri, post_data)
155 body = self.deserialize_single(body)
156 self.expected_success(200, resp.status)
157 return service_client.ResponseBody(resp, body)
161 def __getattr__(self, name):
162 method_prefixes = ["list_", "delete_", "show_", "create_", "update_"]
163 method_functors = [self._lister,
168 for index, prefix in enumerate(method_prefixes):
169 prefix_len = len(prefix)
170 if name[:prefix_len] == prefix:
171 return method_functors[index](name[prefix_len:])
172 raise AttributeError(name)
174 # Common methods that are hard to automate
175 def create_bulk_network(self, names, shared=False):
176 network_list = [{'name': name, 'shared': shared} for name in names]
177 post_data = {'networks': network_list}
178 body = self.serialize_list(post_data, "networks", "network")
179 uri = self.get_uri("networks")
180 resp, body = self.post(uri, body)
181 body = {'networks': self.deserialize_list(body)}
182 self.expected_success(201, resp.status)
183 return service_client.ResponseBody(resp, body)
185 def create_bulk_subnet(self, subnet_list):
186 post_data = {'subnets': subnet_list}
187 body = self.serialize_list(post_data, 'subnets', 'subnet')
188 uri = self.get_uri('subnets')
189 resp, body = self.post(uri, body)
190 body = {'subnets': self.deserialize_list(body)}
191 self.expected_success(201, resp.status)
192 return service_client.ResponseBody(resp, body)
194 def create_bulk_port(self, port_list):
195 post_data = {'ports': port_list}
196 body = self.serialize_list(post_data, 'ports', 'port')
197 uri = self.get_uri('ports')
198 resp, body = self.post(uri, body)
199 body = {'ports': self.deserialize_list(body)}
200 self.expected_success(201, resp.status)
201 return service_client.ResponseBody(resp, body)
203 def wait_for_resource_deletion(self, resource_type, id):
204 """Waits for a resource to be deleted."""
205 start_time = int(time.time())
207 if self.is_resource_deleted(resource_type, id):
209 if int(time.time()) - start_time >= self.build_timeout:
210 raise exceptions.TimeoutException
211 time.sleep(self.build_interval)
213 def is_resource_deleted(self, resource_type, id):
214 method = 'show_' + resource_type
216 getattr(self, method)(id)
217 except AttributeError:
218 raise Exception("Unknown resource type %s " % resource_type)
219 except lib_exc.NotFound:
223 def wait_for_resource_status(self, fetch, status, interval=None,
226 @summary: Waits for a network resource to reach a status
227 @param fetch: the callable to be used to query the resource status
228 @type fecth: callable that takes no parameters and returns the resource
229 @param status: the status that the resource has to reach
231 @param interval: the number of seconds to wait between each status
233 @type interval: Integer
234 @param timeout: the maximum number of seconds to wait for the resource
235 to reach the desired status
236 @type timeout: Integer
239 interval = self.build_interval
241 timeout = self.build_timeout
242 start_time = time.time()
244 while time.time() - start_time <= timeout:
246 if resource['status'] == status:
250 # At this point, the wait has timed out
251 message = 'Resource %s' % (str(resource))
252 message += ' failed to reach status %s' % status
253 message += ' (current: %s)' % resource['status']
254 message += ' within the required time %s' % timeout
255 caller = misc.find_test_caller()
257 message = '(%s) %s' % (caller, message)
258 raise exceptions.TimeoutException(message)
260 def deserialize_single(self, body):
261 return json.loads(body)
263 def deserialize_list(self, body):
264 res = json.loads(body)
265 # expecting response in form
266 # {'resources': [ res1, res2] } => when pagination disabled
267 # {'resources': [..], 'resources_links': {}} => if pagination enabled
269 if k.endswith("_links"):
273 def serialize(self, data):
274 return json.dumps(data)
276 def serialize_list(self, data, root=None, item=None):
277 return self.serialize(data)
279 def update_quotas(self, tenant_id, **kwargs):
280 put_body = {'quota': kwargs}
281 body = json.dumps(put_body)
282 uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
283 resp, body = self.put(uri, body)
284 self.expected_success(200, resp.status)
285 body = json.loads(body)
286 return service_client.ResponseBody(resp, body['quota'])
288 def reset_quotas(self, tenant_id):
289 uri = '%s/quotas/%s' % (self.uri_prefix, tenant_id)
290 resp, body = self.delete(uri)
291 self.expected_success(204, resp.status)
292 return service_client.ResponseBody(resp, body)
294 def create_router(self, name, admin_state_up=True, **kwargs):
295 post_body = {'router': kwargs}
296 post_body['router']['name'] = name
297 post_body['router']['admin_state_up'] = admin_state_up
298 body = json.dumps(post_body)
299 uri = '%s/routers' % (self.uri_prefix)
300 resp, body = self.post(uri, body)
301 self.expected_success(201, resp.status)
302 body = json.loads(body)
303 return service_client.ResponseBody(resp, body)
305 def _update_router(self, router_id, set_enable_snat, **kwargs):
306 uri = '%s/routers/%s' % (self.uri_prefix, router_id)
307 resp, body = self.get(uri)
308 self.expected_success(200, resp.status)
309 body = json.loads(body)
311 update_body['name'] = kwargs.get('name', body['router']['name'])
312 update_body['admin_state_up'] = kwargs.get(
313 'admin_state_up', body['router']['admin_state_up'])
314 cur_gw_info = body['router']['external_gateway_info']
316 # TODO(kevinbenton): setting the external gateway info is not
317 # allowed for a regular tenant. If the ability to update is also
318 # merged, a test case for this will need to be added similar to
320 cur_gw_info.pop('external_fixed_ips', None)
321 if not set_enable_snat:
322 cur_gw_info.pop('enable_snat', None)
323 update_body['external_gateway_info'] = kwargs.get(
324 'external_gateway_info', body['router']['external_gateway_info'])
325 if 'distributed' in kwargs:
326 update_body['distributed'] = kwargs['distributed']
327 update_body = dict(router=update_body)
328 update_body = json.dumps(update_body)
329 resp, body = self.put(uri, update_body)
330 self.expected_success(200, resp.status)
331 body = json.loads(body)
332 return service_client.ResponseBody(resp, body)
334 def update_router(self, router_id, **kwargs):
335 """Update a router leaving enable_snat to its default value."""
336 # If external_gateway_info contains enable_snat the request will fail
337 # with 404 unless executed with admin client, and therefore we instruct
338 # _update_router to not set this attribute
339 # NOTE(salv-orlando): The above applies as long as Neutron's default
340 # policy is to restrict enable_snat usage to admins only.
341 return self._update_router(router_id, set_enable_snat=False, **kwargs)
343 def update_router_with_snat_gw_info(self, router_id, **kwargs):
344 """Update a router passing also the enable_snat attribute.
346 This method must be execute with admin credentials, otherwise the API
347 call will return a 404 error.
349 return self._update_router(router_id, set_enable_snat=True, **kwargs)
351 def add_router_interface_with_subnet_id(self, router_id, subnet_id):
352 uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
354 update_body = {"subnet_id": subnet_id}
355 update_body = json.dumps(update_body)
356 resp, body = self.put(uri, update_body)
357 self.expected_success(200, resp.status)
358 body = json.loads(body)
359 return service_client.ResponseBody(resp, body)
361 def add_router_interface_with_port_id(self, router_id, port_id):
362 uri = '%s/routers/%s/add_router_interface' % (self.uri_prefix,
364 update_body = {"port_id": port_id}
365 update_body = json.dumps(update_body)
366 resp, body = self.put(uri, update_body)
367 self.expected_success(200, resp.status)
368 body = json.loads(body)
369 return service_client.ResponseBody(resp, body)
371 def remove_router_interface_with_subnet_id(self, router_id, subnet_id):
372 uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
374 update_body = {"subnet_id": subnet_id}
375 update_body = json.dumps(update_body)
376 resp, body = self.put(uri, update_body)
377 self.expected_success(200, resp.status)
378 body = json.loads(body)
379 return service_client.ResponseBody(resp, body)
381 def remove_router_interface_with_port_id(self, router_id, port_id):
382 uri = '%s/routers/%s/remove_router_interface' % (self.uri_prefix,
384 update_body = {"port_id": port_id}
385 update_body = json.dumps(update_body)
386 resp, body = self.put(uri, update_body)
387 self.expected_success(200, resp.status)
388 body = json.loads(body)
389 return service_client.ResponseBody(resp, body)
391 def list_router_interfaces(self, uuid):
392 uri = '%s/ports?device_id=%s' % (self.uri_prefix, uuid)
393 resp, body = self.get(uri)
394 self.expected_success(200, resp.status)
395 body = json.loads(body)
396 return service_client.ResponseBody(resp, body)
398 def update_agent(self, agent_id, agent_info):
400 :param agent_info: Agent update information.
401 E.g {"admin_state_up": True}
403 uri = '%s/agents/%s' % (self.uri_prefix, agent_id)
404 agent = {"agent": agent_info}
405 body = json.dumps(agent)
406 resp, body = self.put(uri, body)
407 self.expected_success(200, resp.status)
408 body = json.loads(body)
409 return service_client.ResponseBody(resp, body)
411 def list_routers_on_l3_agent(self, agent_id):
412 uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
413 resp, body = self.get(uri)
414 self.expected_success(200, resp.status)
415 body = json.loads(body)
416 return service_client.ResponseBody(resp, body)
418 def list_l3_agents_hosting_router(self, router_id):
419 uri = '%s/routers/%s/l3-agents' % (self.uri_prefix, router_id)
420 resp, body = self.get(uri)
421 self.expected_success(200, resp.status)
422 body = json.loads(body)
423 return service_client.ResponseBody(resp, body)
425 def add_router_to_l3_agent(self, agent_id, router_id):
426 uri = '%s/agents/%s/l3-routers' % (self.uri_prefix, agent_id)
427 post_body = {"router_id": router_id}
428 body = json.dumps(post_body)
429 resp, body = self.post(uri, body)
430 self.expected_success(201, resp.status)
431 body = json.loads(body)
432 return service_client.ResponseBody(resp, body)
434 def remove_router_from_l3_agent(self, agent_id, router_id):
435 uri = '%s/agents/%s/l3-routers/%s' % (
436 self.uri_prefix, agent_id, router_id)
437 resp, body = self.delete(uri)
438 self.expected_success(204, resp.status)
439 return service_client.ResponseBody(resp, body)
441 def list_dhcp_agent_hosting_network(self, network_id):
442 uri = '%s/networks/%s/dhcp-agents' % (self.uri_prefix, network_id)
443 resp, body = self.get(uri)
444 self.expected_success(200, resp.status)
445 body = json.loads(body)
446 return service_client.ResponseBody(resp, body)
448 def list_networks_hosted_by_one_dhcp_agent(self, agent_id):
449 uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id)
450 resp, body = self.get(uri)
451 self.expected_success(200, resp.status)
452 body = json.loads(body)
453 return service_client.ResponseBody(resp, body)
455 def remove_network_from_dhcp_agent(self, agent_id, network_id):
456 uri = '%s/agents/%s/dhcp-networks/%s' % (self.uri_prefix, agent_id,
458 resp, body = self.delete(uri)
459 self.expected_success(204, resp.status)
460 return service_client.ResponseBody(resp, body)
462 def create_ikepolicy(self, name, **kwargs):
468 for key, val in kwargs.items():
469 post_body['ikepolicy'][key] = val
470 body = json.dumps(post_body)
471 uri = '%s/vpn/ikepolicies' % (self.uri_prefix)
472 resp, body = self.post(uri, body)
473 self.expected_success(201, resp.status)
474 body = json.loads(body)
475 return service_client.ResponseBody(resp, body)
477 def update_extra_routes(self, router_id, nexthop, destination):
478 uri = '%s/routers/%s' % (self.uri_prefix, router_id)
481 'routes': [{'nexthop': nexthop,
482 "destination": destination}]
485 body = json.dumps(put_body)
486 resp, body = self.put(uri, body)
487 self.expected_success(200, resp.status)
488 body = json.loads(body)
489 return service_client.ResponseBody(resp, body)
491 def delete_extra_routes(self, router_id):
492 uri = '%s/routers/%s' % (self.uri_prefix, router_id)
496 'routes': null_routes
499 body = json.dumps(put_body)
500 resp, body = self.put(uri, body)
501 self.expected_success(200, resp.status)
502 body = json.loads(body)
503 return service_client.ResponseBody(resp, body)
505 def add_dhcp_agent_to_network(self, agent_id, network_id):
506 post_body = {'network_id': network_id}
507 body = json.dumps(post_body)
508 uri = '%s/agents/%s/dhcp-networks' % (self.uri_prefix, agent_id)
509 resp, body = self.post(uri, body)
510 self.expected_success(201, resp.status)
511 body = json.loads(body)
512 return service_client.ResponseBody(resp, body)
514 def insert_firewall_rule_in_policy(self, firewall_policy_id,
515 firewall_rule_id, insert_after="",
517 uri = '%s/fw/firewall_policies/%s/insert_rule' % (self.uri_prefix,
520 "firewall_rule_id": firewall_rule_id,
521 "insert_after": insert_after,
522 "insert_before": insert_before
524 body = json.dumps(body)
525 resp, body = self.put(uri, body)
526 self.expected_success(200, resp.status)
527 body = json.loads(body)
528 return service_client.ResponseBody(resp, body)
530 def remove_firewall_rule_from_policy(self, firewall_policy_id,
532 uri = '%s/fw/firewall_policies/%s/remove_rule' % (self.uri_prefix,
534 update_body = {"firewall_rule_id": firewall_rule_id}
535 update_body = json.dumps(update_body)
536 resp, body = self.put(uri, update_body)
537 self.expected_success(200, resp.status)
538 body = json.loads(body)
539 return service_client.ResponseBody(resp, body)
541 def list_qos_policies(self, **filters):
543 uri = '%s/qos/policies?%s' % (self.uri_prefix,
544 urlparse.urlencode(filters))
546 uri = '%s/qos/policies' % self.uri_prefix
547 resp, body = self.get(uri)
548 self.expected_success(200, resp.status)
549 body = json.loads(body)
550 return service_client.ResponseBody(resp, body)
552 def create_qos_policy(self, name, description, shared, tenant_id=None):
553 uri = '%s/qos/policies' % self.uri_prefix
554 post_data = {'policy': {
556 'description': description,
559 if tenant_id is not None:
560 post_data['policy']['tenant_id'] = tenant_id
561 resp, body = self.post(uri, self.serialize(post_data))
562 body = self.deserialize_single(body)
563 self.expected_success(201, resp.status)
564 return service_client.ResponseBody(resp, body)
566 def update_qos_policy(self, policy_id, **kwargs):
567 uri = '%s/qos/policies/%s' % (self.uri_prefix, policy_id)
568 post_data = self.serialize({'policy': kwargs})
569 resp, body = self.put(uri, post_data)
570 body = self.deserialize_single(body)
571 self.expected_success(200, resp.status)
572 return service_client.ResponseBody(resp, body)
574 def create_bandwidth_limit_rule(self, policy_id, max_kbps, max_burst_kbps):
575 uri = '%s/qos/policies/%s/bandwidth_limit_rules' % (
576 self.uri_prefix, policy_id)
577 post_data = self.serialize(
578 {'bandwidth_limit_rule': {
579 'max_kbps': max_kbps,
580 'max_burst_kbps': max_burst_kbps}
582 resp, body = self.post(uri, post_data)
583 self.expected_success(201, resp.status)
584 body = json.loads(body)
585 return service_client.ResponseBody(resp, body)
587 def list_bandwidth_limit_rules(self, policy_id):
588 uri = '%s/qos/policies/%s/bandwidth_limit_rules' % (
589 self.uri_prefix, policy_id)
590 resp, body = self.get(uri)
591 body = self.deserialize_single(body)
592 self.expected_success(200, resp.status)
593 return service_client.ResponseBody(resp, body)
595 def show_bandwidth_limit_rule(self, policy_id, rule_id):
596 uri = '%s/qos/policies/%s/bandwidth_limit_rules/%s' % (
597 self.uri_prefix, policy_id, rule_id)
598 resp, body = self.get(uri)
599 body = self.deserialize_single(body)
600 self.expected_success(200, resp.status)
601 return service_client.ResponseBody(resp, body)
603 def update_bandwidth_limit_rule(self, policy_id, rule_id, **kwargs):
604 uri = '%s/qos/policies/%s/bandwidth_limit_rules/%s' % (
605 self.uri_prefix, policy_id, rule_id)
606 post_data = {'bandwidth_limit_rule': kwargs}
607 resp, body = self.put(uri, json.dumps(post_data))
608 body = self.deserialize_single(body)
609 self.expected_success(200, resp.status)
610 return service_client.ResponseBody(resp, body)
612 def delete_bandwidth_limit_rule(self, policy_id, rule_id):
613 uri = '%s/qos/policies/%s/bandwidth_limit_rules/%s' % (
614 self.uri_prefix, policy_id, rule_id)
615 resp, body = self.delete(uri)
616 self.expected_success(204, resp.status)
617 return service_client.ResponseBody(resp, body)
619 def list_qos_rule_types(self):
620 uri = '%s/qos/rule-types' % self.uri_prefix
621 resp, body = self.get(uri)
622 self.expected_success(200, resp.status)
623 body = json.loads(body)
624 return service_client.ResponseBody(resp, body)