nithya-ganesan | 222efd7 | 2015-01-22 12:20:27 +0000 | [diff] [blame] | 1 | # Copyright (c) 2015 Hewlett-Packard Development Company, L.P. |
| 2 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 3 | # you may not use this file except in compliance with the License. |
| 4 | # You may obtain a copy of the License at |
| 5 | # |
| 6 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 7 | # |
| 8 | # Unless required by applicable law or agreed to in writing, software |
| 9 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 11 | # See the License for the specific language governing permissions and |
| 12 | # limitations under the License. |
| 13 | |
| 14 | from oslo_log import log as logging |
| 15 | |
Ken'ichi Ohmichi | ef1c1ce | 2017-03-10 11:07:10 -0800 | [diff] [blame] | 16 | from tempest.lib.common.utils import data_utils |
Andrea Frittoli (andreaf) | db9672e | 2016-02-23 14:07:24 -0500 | [diff] [blame] | 17 | from tempest.lib import exceptions as lib_exc |
Andrea Frittoli (andreaf) | 8def7ca | 2015-05-13 14:24:19 +0100 | [diff] [blame] | 18 | |
nithya-ganesan | 222efd7 | 2015-01-22 12:20:27 +0000 | [diff] [blame] | 19 | LOG = logging.getLogger(__name__) |
| 20 | |
| 21 | |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 22 | def _network_service(clients, use_neutron): |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 23 | # Internal helper to select the right network clients |
Andrea Frittoli | 463a8a6 | 2017-08-09 16:55:33 +0100 | [diff] [blame] | 24 | if use_neutron: |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 25 | return clients.network |
Andrea Frittoli | 463a8a6 | 2017-08-09 16:55:33 +0100 | [diff] [blame] | 26 | else: |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 27 | return clients.compute |
Matthew Treinish | 861619c | 2016-06-16 17:11:49 -0400 | [diff] [blame] | 28 | |
| 29 | |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 30 | def create_ssh_security_group(clients, add_rule=False, ethertype='IPv4', |
Andrea Frittoli | 1fa7a60 | 2017-08-09 16:28:55 +0100 | [diff] [blame] | 31 | use_neutron=True): |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 32 | """Create a security group for ping/ssh testing |
| 33 | |
| 34 | Create a security group to be attached to a VM using the nova or neutron |
| 35 | clients. If rules are added, the group can be attached to a VM to enable |
| 36 | connectivity validation over ICMP and further testing over SSH. |
| 37 | |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 38 | :param clients: Instance of `tempest.lib.services.clients.ServiceClients` |
| 39 | or of a subclass of it. Resources are provisioned using clients from |
| 40 | `clients`. |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 41 | :param add_rule: Whether security group rules are provisioned or not. |
| 42 | Defaults to `False`. |
| 43 | :param ethertype: 'IPv4' or 'IPv6'. Honoured only in case neutron is used. |
| 44 | :param use_neutron: When True resources are provisioned via neutron, when |
| 45 | False resources are provisioned via nova. |
| 46 | :returns: A dictionary with the security group as returned by the API. |
| 47 | |
| 48 | Examples:: |
| 49 | |
| 50 | from tempest.common import validation_resources as vr |
| 51 | from tempest.lib import auth |
| 52 | from tempest.lib.services import clients |
| 53 | |
| 54 | creds = auth.get_credentials('http://mycloud/identity/v3', |
| 55 | username='me', project_name='me', |
| 56 | password='secret', domain_name='Default') |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 57 | osclients = clients.ServiceClients(creds, 'http://mycloud/identity/v3') |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 58 | # Security group for IPv4 tests |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 59 | sg4 = vr.create_ssh_security_group(osclients, add_rule=True) |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 60 | # Security group for IPv6 tests |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 61 | sg6 = vr.create_ssh_security_group(osclients, ethertype='IPv6', |
| 62 | add_rule=True) |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 63 | """ |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 64 | network_service = _network_service(clients, use_neutron) |
Andrea Frittoli | 463a8a6 | 2017-08-09 16:55:33 +0100 | [diff] [blame] | 65 | security_groups_client = network_service.SecurityGroupsClient() |
| 66 | security_group_rules_client = network_service.SecurityGroupRulesClient() |
| 67 | # Security Group clients for nova and neutron behave the same |
nithya-ganesan | 222efd7 | 2015-01-22 12:20:27 +0000 | [diff] [blame] | 68 | sg_name = data_utils.rand_name('securitygroup-') |
| 69 | sg_description = data_utils.rand_name('description-') |
Yaroslav Lobankov | e5cc9fb | 2015-08-07 17:30:51 +0300 | [diff] [blame] | 70 | security_group = security_groups_client.create_security_group( |
ghanshyam | b610b77 | 2015-08-24 17:29:38 +0900 | [diff] [blame] | 71 | name=sg_name, description=sg_description)['security_group'] |
Andrea Frittoli | 463a8a6 | 2017-08-09 16:55:33 +0100 | [diff] [blame] | 72 | # Security Group Rules clients require different parameters depending on |
| 73 | # the network service in use |
nithya-ganesan | 222efd7 | 2015-01-22 12:20:27 +0000 | [diff] [blame] | 74 | if add_rule: |
Andrea Frittoli | 1fa7a60 | 2017-08-09 16:28:55 +0100 | [diff] [blame] | 75 | if use_neutron: |
Andrea Frittoli | 463a8a6 | 2017-08-09 16:55:33 +0100 | [diff] [blame] | 76 | security_group_rules_client.create_security_group_rule( |
| 77 | security_group_id=security_group['id'], |
| 78 | protocol='tcp', |
| 79 | ethertype=ethertype, |
| 80 | port_range_min=22, |
| 81 | port_range_max=22, |
| 82 | direction='ingress') |
| 83 | security_group_rules_client.create_security_group_rule( |
| 84 | security_group_id=security_group['id'], |
| 85 | protocol='icmp', |
| 86 | ethertype=ethertype, |
| 87 | direction='ingress') |
Matthew Treinish | 861619c | 2016-06-16 17:11:49 -0400 | [diff] [blame] | 88 | else: |
| 89 | security_group_rules_client.create_security_group_rule( |
| 90 | parent_group_id=security_group['id'], ip_protocol='tcp', |
| 91 | from_port=22, to_port=22) |
| 92 | security_group_rules_client.create_security_group_rule( |
| 93 | parent_group_id=security_group['id'], ip_protocol='icmp', |
| 94 | from_port=-1, to_port=-1) |
nithya-ganesan | 222efd7 | 2015-01-22 12:20:27 +0000 | [diff] [blame] | 95 | LOG.debug("SSH Validation resource security group with tcp and icmp " |
Jordan Pittier | 525ec71 | 2016-12-07 17:51:26 +0100 | [diff] [blame] | 96 | "rules %s created", sg_name) |
nithya-ganesan | 222efd7 | 2015-01-22 12:20:27 +0000 | [diff] [blame] | 97 | return security_group |
| 98 | |
| 99 | |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 100 | def create_validation_resources(clients, keypair=False, floating_ip=False, |
| 101 | security_group=False, |
| 102 | security_group_rules=False, |
Andrea Frittoli | 1fa7a60 | 2017-08-09 16:28:55 +0100 | [diff] [blame] | 103 | ethertype='IPv4', use_neutron=True, |
| 104 | floating_network_id=None, |
| 105 | floating_network_name=None): |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 106 | """Provision resources for VM ping/ssh testing |
| 107 | |
| 108 | Create resources required to be able to ping / ssh a virtual machine: |
| 109 | keypair, security group, security group rules and a floating IP. |
| 110 | Which of those resources are required may depend on the cloud setup and on |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 111 | the specific test and it can be controlled via the corresponding |
| 112 | arguments. |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 113 | |
| 114 | Provisioned resources are returned in a dictionary. |
| 115 | |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 116 | :param clients: Instance of `tempest.lib.services.clients.ServiceClients` |
| 117 | or of a subclass of it. Resources are provisioned using clients from |
| 118 | `clients`. |
| 119 | :param keypair: Whether to provision a keypair. Defaults to False. |
| 120 | :param floating_ip: Whether to provision a floating IP. Defaults to False. |
| 121 | :param security_group: Whether to provision a security group. Defaults to |
| 122 | False. |
| 123 | :param security_group_rules: Whether to provision security group rules. |
| 124 | Defaults to False. |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 125 | :param ethertype: 'IPv4' or 'IPv6'. Honoured only in case neutron is used. |
| 126 | :param use_neutron: When True resources are provisioned via neutron, when |
| 127 | False resources are provisioned via nova. |
| 128 | :param floating_network_id: The id of the network used to provision a |
| 129 | floating IP. Only used if a floating IP is requested and with neutron. |
| 130 | :param floating_network_name: The name of the floating IP pool used to |
| 131 | provision the floating IP. Only used if a floating IP is requested and |
| 132 | with nova-net. |
| 133 | :returns: A dictionary with the same keys as the input |
| 134 | `validation_resources` and the resources for values in the format |
| 135 | they are returned by the API. |
| 136 | |
| 137 | Examples:: |
| 138 | |
| 139 | from tempest.common import validation_resources as vr |
| 140 | from tempest.lib import auth |
| 141 | from tempest.lib.services import clients |
| 142 | |
| 143 | creds = auth.get_credentials('http://mycloud/identity/v3', |
| 144 | username='me', project_name='me', |
| 145 | password='secret', domain_name='Default') |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 146 | osclients = clients.ServiceClients(creds, 'http://mycloud/identity/v3') |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 147 | # Request keypair and floating IP |
| 148 | resources = dict(keypair=True, security_group=False, |
| 149 | security_group_rules=False, floating_ip=True) |
| 150 | resources = vr.create_validation_resources( |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 151 | osclients, use_neutron=True, |
| 152 | floating_network_id='4240E68E-23DA-4C82-AC34-9FEFAA24521C', |
| 153 | **resources) |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 154 | |
| 155 | # The floating IP to be attached to the VM |
| 156 | floating_ip = resources['floating_ip']['ip'] |
| 157 | """ |
nithya-ganesan | 222efd7 | 2015-01-22 12:20:27 +0000 | [diff] [blame] | 158 | # Create and Return the validation resources required to validate a VM |
| 159 | validation_data = {} |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 160 | if keypair: |
| 161 | keypair_name = data_utils.rand_name('keypair') |
| 162 | validation_data.update( |
| 163 | clients.compute.KeyPairsClient().create_keypair( |
ghanshyam | dee01f2 | 2015-08-17 11:41:47 +0900 | [diff] [blame] | 164 | name=keypair_name)) |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 165 | LOG.debug("Validation resource key %s created", keypair_name) |
| 166 | if security_group: |
| 167 | validation_data['security_group'] = create_ssh_security_group( |
| 168 | clients, add_rule=security_group_rules, |
| 169 | use_neutron=use_neutron, ethertype=ethertype) |
| 170 | if floating_ip: |
| 171 | floating_ip_client = _network_service( |
| 172 | clients, use_neutron).FloatingIPsClient() |
| 173 | if use_neutron: |
| 174 | floatingip = floating_ip_client.create_floatingip( |
| 175 | floating_network_id=floating_network_id) |
| 176 | # validation_resources['floating_ip'] has historically looked |
| 177 | # like a compute API POST /os-floating-ips response, so we need |
| 178 | # to mangle it a bit for a Neutron response with different |
| 179 | # fields. |
| 180 | validation_data['floating_ip'] = floatingip['floatingip'] |
| 181 | validation_data['floating_ip']['ip'] = ( |
| 182 | floatingip['floatingip']['floating_ip_address']) |
| 183 | else: |
| 184 | # NOTE(mriedem): The os-floating-ips compute API was deprecated |
| 185 | # in the 2.36 microversion. Any tests for CRUD operations on |
| 186 | # floating IPs using the compute API should be capped at 2.35. |
| 187 | validation_data.update(floating_ip_client.create_floating_ip( |
| 188 | pool=floating_network_name)) |
nithya-ganesan | 222efd7 | 2015-01-22 12:20:27 +0000 | [diff] [blame] | 189 | return validation_data |
| 190 | |
| 191 | |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 192 | def clear_validation_resources(clients, keypair=None, floating_ip=None, |
| 193 | security_group=None, use_neutron=True): |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 194 | """Cleanup resources for VM ping/ssh testing |
| 195 | |
| 196 | Cleanup a set of resources provisioned via `create_validation_resources`. |
| 197 | In case of errors during cleanup, the exception is logged and the cleanup |
| 198 | process is continued. The first exception that was raised is re-raised |
| 199 | after the cleanup is complete. |
| 200 | |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 201 | :param clients: Instance of `tempest.lib.services.clients.ServiceClients` |
| 202 | or of a subclass of it. Resources are provisioned using clients from |
| 203 | `clients`. |
| 204 | :param keypair: A dictionary with the keypair to be deleted. Defaults to |
| 205 | None. |
| 206 | :param floating_ip: A dictionary with the floating_ip to be deleted. |
| 207 | Defaults to None. |
| 208 | :param security_group: A dictionary with the security_group to be deleted. |
| 209 | Defaults to None. |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 210 | :param use_neutron: When True resources are provisioned via neutron, when |
| 211 | False resources are provisioned via nova. |
| 212 | :returns: A dictionary with the same keys as the input |
| 213 | `validation_resources` and the resources for values in the format |
| 214 | they are returned by the API. |
| 215 | |
| 216 | Examples:: |
| 217 | |
| 218 | from tempest.common import validation_resources as vr |
| 219 | from tempest.lib import auth |
| 220 | from tempest.lib.services import clients |
| 221 | |
| 222 | creds = auth.get_credentials('http://mycloud/identity/v3', |
| 223 | username='me', project_name='me', |
| 224 | password='secret', domain_name='Default') |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 225 | osclients = clients.ServiceClients(creds, 'http://mycloud/identity/v3') |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 226 | # Request keypair and floating IP |
| 227 | resources = dict(keypair=True, security_group=False, |
| 228 | security_group_rules=False, floating_ip=True) |
| 229 | resources = vr.create_validation_resources( |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 230 | osclients, validation_resources=resources, use_neutron=True, |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 231 | floating_network_id='4240E68E-23DA-4C82-AC34-9FEFAA24521C') |
| 232 | |
| 233 | # Now cleanup the resources |
| 234 | try: |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 235 | vr.clear_validation_resources(osclients, use_neutron=True, |
| 236 | **resources) |
Andrea Frittoli | 557320e | 2017-08-09 21:08:08 +0100 | [diff] [blame] | 237 | except Exception as e: |
| 238 | LOG.exception('Something went wrong during cleanup, ignoring') |
| 239 | """ |
nithya-ganesan | 222efd7 | 2015-01-22 12:20:27 +0000 | [diff] [blame] | 240 | has_exception = None |
Andrea Frittoli | 8871fca | 2017-08-10 23:43:25 +0100 | [diff] [blame] | 241 | if keypair: |
| 242 | keypair_client = clients.compute.KeyPairsClient() |
| 243 | keypair_name = keypair['name'] |
| 244 | try: |
| 245 | keypair_client.delete_keypair(keypair_name) |
| 246 | except lib_exc.NotFound: |
| 247 | LOG.warning( |
| 248 | "Keypair %s is not found when attempting to delete", |
| 249 | keypair_name |
| 250 | ) |
| 251 | except Exception as exc: |
| 252 | LOG.exception('Exception raised while deleting key %s', |
| 253 | keypair_name) |
| 254 | if not has_exception: |
| 255 | has_exception = exc |
| 256 | network_service = _network_service(clients, use_neutron) |
| 257 | if security_group: |
| 258 | security_group_client = network_service.SecurityGroupsClient() |
| 259 | sec_id = security_group['id'] |
| 260 | try: |
| 261 | security_group_client.delete_security_group(sec_id) |
| 262 | security_group_client.wait_for_resource_deletion(sec_id) |
| 263 | except lib_exc.NotFound: |
| 264 | LOG.warning("Security group %s is not found when attempting " |
| 265 | "to delete", sec_id) |
| 266 | except lib_exc.Conflict as exc: |
| 267 | LOG.exception('Conflict while deleting security ' |
| 268 | 'group %s VM might not be deleted', sec_id) |
| 269 | if not has_exception: |
| 270 | has_exception = exc |
| 271 | except Exception as exc: |
| 272 | LOG.exception('Exception raised while deleting security ' |
| 273 | 'group %s', sec_id) |
| 274 | if not has_exception: |
| 275 | has_exception = exc |
| 276 | if floating_ip: |
| 277 | floating_ip_client = network_service.FloatingIPsClient() |
| 278 | fip_id = floating_ip['id'] |
| 279 | try: |
| 280 | if use_neutron: |
| 281 | floating_ip_client.delete_floatingip(fip_id) |
| 282 | else: |
| 283 | floating_ip_client.delete_floating_ip(fip_id) |
| 284 | except lib_exc.NotFound: |
| 285 | LOG.warning('Floating ip %s not found while attempting to ' |
| 286 | 'delete', fip_id) |
| 287 | except Exception as exc: |
| 288 | LOG.exception('Exception raised while deleting ip %s', fip_id) |
| 289 | if not has_exception: |
| 290 | has_exception = exc |
nithya-ganesan | 222efd7 | 2015-01-22 12:20:27 +0000 | [diff] [blame] | 291 | if has_exception: |
| 292 | raise has_exception |