blob: d00210c41271d78052ce05d29e945fba7a71bc4a [file] [log] [blame]
Itzik Brown1ef813a2016-06-06 12:56:21 +00001# Copyright 2016 Red Hat, Inc.
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15import errno
16import socket
17import time
18
Chandan Kumarc125fd12017-11-15 19:41:01 +053019from neutron_lib.services.qos import constants as qos_consts
Itzik Brown1ef813a2016-06-06 12:56:21 +000020from oslo_log import log as logging
Chandan Kumarc125fd12017-11-15 19:41:01 +053021from tempest.common import utils as tutils
nfridman55f2ee62019-12-17 03:06:12 -050022from tempest.common import waiters
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +000023from tempest.lib import decorators
Itzik Brown1ef813a2016-06-06 12:56:21 +000024
Chandan Kumar667d3d32017-09-22 12:24:06 +053025from neutron_tempest_plugin.api import base as base_api
Chandan Kumar667d3d32017-09-22 12:24:06 +053026from neutron_tempest_plugin.common import ssh
27from neutron_tempest_plugin.common import utils
28from neutron_tempest_plugin import config
29from neutron_tempest_plugin.scenario import base
30from neutron_tempest_plugin.scenario import constants
31from neutron_tempest_plugin.scenario import exceptions as sc_exceptions
Itzik Brown1ef813a2016-06-06 12:56:21 +000032
33CONF = config.CONF
34LOG = logging.getLogger(__name__)
35
36
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000037def _try_connect(host_ip, port, socket_timeout):
Itzik Brown1ef813a2016-06-06 12:56:21 +000038 try:
39 client_socket = socket.socket(socket.AF_INET,
40 socket.SOCK_STREAM)
41 client_socket.connect((host_ip, port))
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000042 client_socket.settimeout(socket_timeout)
Itzik Brown1ef813a2016-06-06 12:56:21 +000043 return client_socket
44 except socket.error as serr:
45 if serr.errno == errno.ECONNREFUSED:
46 raise sc_exceptions.SocketConnectionRefused(host=host_ip,
47 port=port)
48 else:
49 raise
50
51
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000052def _connect_socket(host, port, socket_timeout):
Brian Haleyaee61ac2018-10-09 20:00:27 -040053 """Try to initiate a connection to a host using an ip address and a port.
Itzik Brown1ef813a2016-06-06 12:56:21 +000054
55 Trying couple of times until a timeout is reached in case the listening
56 host is not ready yet.
57 """
58
59 start = time.time()
60 while True:
61 try:
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000062 return _try_connect(host, port, socket_timeout)
Itzik Brown1ef813a2016-06-06 12:56:21 +000063 except sc_exceptions.SocketConnectionRefused:
64 if time.time() - start > constants.SOCKET_CONNECT_TIMEOUT:
65 raise sc_exceptions.ConnectionTimeoutException(host=host,
66 port=port)
67
68
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +090069class QoSTestMixin(object):
Itzik Brown1ef813a2016-06-06 12:56:21 +000070 credentials = ['primary', 'admin']
71 force_tenant_isolation = False
72
Itzik Brown1ef813a2016-06-06 12:56:21 +000073 TOLERANCE_FACTOR = 1.5
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000074 BUFFER_SIZE = 512
Brian Haley33ef4602018-04-26 14:37:49 -040075 LIMIT_BYTES_SEC = (constants.LIMIT_KILO_BITS_PER_SECOND * 1024 *
76 TOLERANCE_FACTOR / 8.0)
LIU Yulong5ba88ef2017-12-22 10:50:15 +080077 NC_PORT = 1234
Maciej Józefczyk41b80192020-03-03 17:10:57 +010078 DOWNLOAD_DURATION = 5
79 # NOTE(mjozefcz): This makes around 10 retries.
80 CHECK_TIMEOUT = DOWNLOAD_DURATION * 10
Itzik Brown1ef813a2016-06-06 12:56:21 +000081
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000082 def _check_bw(self, ssh_client, host, port, expected_bw=LIMIT_BYTES_SEC):
Maciej Józefczyk328edc82019-09-16 14:05:48 +000083 utils.kill_nc_process(ssh_client)
Slawek Kaplonskifd4141f2020-03-14 14:34:00 +010084 self.ensure_nc_listen(ssh_client, port, "tcp")
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +010085
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +030086 # Open TCP socket to remote VM and download big file
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +010087 start_time = time.time()
Maciej Józefczyk41b80192020-03-03 17:10:57 +010088 client_socket = _connect_socket(
89 host, port, constants.SOCKET_CONNECT_TIMEOUT)
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +010090 total_bytes_read = 0
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000091 try:
Maciej Józefczyk41b80192020-03-03 17:10:57 +010092 while time.time() - start_time < self.DOWNLOAD_DURATION:
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000093 data = client_socket.recv(self.BUFFER_SIZE)
94 total_bytes_read += len(data)
Itzik Brown1ef813a2016-06-06 12:56:21 +000095
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000096 # Calculate and return actual BW + logging result
97 time_elapsed = time.time() - start_time
98 bytes_per_second = total_bytes_read / time_elapsed
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +010099
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000100 LOG.debug("time_elapsed = %(time_elapsed).16f, "
101 "total_bytes_read = %(total_bytes_read)d, "
Maciej Józefczyk41b80192020-03-03 17:10:57 +0100102 "bytes_per_second = %(bytes_per_second)d, "
103 "expected_bw = %(expected_bw)d.",
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000104 {'time_elapsed': time_elapsed,
105 'total_bytes_read': total_bytes_read,
Maciej Józefczyk41b80192020-03-03 17:10:57 +0100106 'bytes_per_second': bytes_per_second,
107 'expected_bw': expected_bw})
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000108 return bytes_per_second <= expected_bw
109 except socket.timeout:
110 LOG.warning('Socket timeout while reading the remote file, bytes '
111 'read: %s', total_bytes_read)
Maciej Józefczyk328edc82019-09-16 14:05:48 +0000112 utils.kill_nc_process(ssh_client)
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000113 return False
114 finally:
115 client_socket.close()
Itzik Brown1ef813a2016-06-06 12:56:21 +0000116
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800117 def _create_ssh_client(self):
118 return ssh.Client(self.fip['floating_ip_address'],
119 CONF.validation.image_ssh_user,
120 pkey=self.keypair['private_key'])
121
122 def _test_basic_resources(self):
123 self.setup_network_and_server()
124 self.check_connectivity(self.fip['floating_ip_address'],
125 CONF.validation.image_ssh_user,
126 self.keypair['private_key'])
127 rulesets = [{'protocol': 'tcp',
128 'direction': 'ingress',
129 'port_range_min': self.NC_PORT,
130 'port_range_max': self.NC_PORT,
131 'remote_ip_prefix': '0.0.0.0/0'}]
132 self.create_secgroup_rules(rulesets,
133 self.security_groups[-1]['id'])
134
135 def _create_qos_policy(self):
136 policy = self.os_admin.network_client.create_qos_policy(
137 name='test-policy',
138 description='test-qos-policy',
139 shared=True)
Eduardo Olivares98772912021-02-19 12:36:21 +0100140 self.qos_policies.append(policy['policy'])
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800141 return policy['policy']['id']
142
nfridman55f2ee62019-12-17 03:06:12 -0500143 def _create_server_by_port(self, port=None):
144 """Launch an instance using a port interface;
145
146 In case that the given port is None, a new port is created,
147 activated and configured with inbound SSH and TCP connection.
148 """
149 # Create and activate the port that will be assign to the instance.
150 if port is None:
151 secgroup = self.create_security_group()
152 self.create_loginable_secgroup_rule(
153 secgroup_id=secgroup['id'])
154
155 secgroup_rules = [{'protocol': 'tcp',
156 'direction': 'ingress',
157 'port_range_min': self.NC_PORT,
158 'port_range_max': self.NC_PORT,
159 'remote_ip_prefix': '0.0.0.0/0'}]
160
161 self.create_secgroup_rules(secgroup_rules,
162 secgroup['id'])
163
164 port = self.create_port(self.network,
165 security_groups=[secgroup['id']])
166 self.fip = self.create_floatingip(port=port)
167
168 keypair = self.create_keypair()
169
170 server_kwargs = {
171 'flavor_ref': CONF.compute.flavor_ref,
172 'image_ref': CONF.compute.image_ref,
173 'key_name': keypair['name'],
174 'networks': [{'port': port['id']}],
175 }
176
177 server = self.create_server(**server_kwargs)
178 self.wait_for_server_active(server['server'])
Slawek Kaplonski2211eab2020-10-20 16:43:53 +0200179 self.wait_for_guest_os_ready(server['server'])
nfridman55f2ee62019-12-17 03:06:12 -0500180 self.check_connectivity(self.fip['floating_ip_address'],
181 CONF.validation.image_ssh_user,
182 keypair['private_key'])
183 return server, port
184
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900185
186class QoSTest(QoSTestMixin, base.BaseTempestTestCase):
187 @classmethod
188 @tutils.requires_ext(extension="qos", service="network")
189 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
190 def resource_setup(cls):
191 super(QoSTest, cls).resource_setup()
192
Eduardo Olivares98772912021-02-19 12:36:21 +0100193 @classmethod
194 def setup_clients(cls):
195 super(QoSTest, cls).setup_clients()
196 cls.admin_client = cls.os_admin.network_client
197
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300198 @decorators.idempotent_id('00682a0c-b72e-11e8-b81e-8c16450ea513')
199 def test_qos_basic_and_update(self):
Arkady Shtemplerc2b80702020-10-10 15:15:43 +0300200 """This test covers following scenarios:
Itzik Brown1ef813a2016-06-06 12:56:21 +0000201
Arkady Shtemplerc2b80702020-10-10 15:15:43 +0300202 1) Create a QoS policy associated with the network.
203 Expected result: BW is limited according the values set in
204 QoS policy rule.
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300205
Arkady Shtemplerc2b80702020-10-10 15:15:43 +0300206 2) Update QoS policy associated with the network.
207 Expected result: BW is limited according the new values
208 set in QoS policy rule.
209
210 3) Create a new QoS policy associated with the VM port.
211 Expected result: BW is limited according the values set in
212 new QoS policy rule.
213 Note: Neutron port is prioritized higher than Network, means
214 that: "Neutron Port Priority" is also covered.
215
216 4) Update QoS policy associated with the VM port.
217 Expected result: BW is limited according the new values set
218 in QoS policy rule.
219
Itzik Brown1ef813a2016-06-06 12:56:21 +0000220 """
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300221
222 # Setup resources
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800223 self._test_basic_resources()
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800224 ssh_client = self._create_ssh_client()
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300225
226 # Create QoS policy
227 bw_limit_policy_id = self._create_qos_policy()
228
229 # As admin user create QoS rule
230 rule_id = self.os_admin.network_client.create_bandwidth_limit_rule(
231 policy_id=bw_limit_policy_id,
232 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
233 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)[
234 'bandwidth_limit_rule']['id']
235
236 # Associate QoS to the network
237 self.os_admin.network_client.update_network(
238 self.network['id'], qos_policy_id=bw_limit_policy_id)
239
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300240 # Basic test, Check that actual BW while downloading file
241 # is as expected (Original BW)
Itzik Brown1ef813a2016-06-06 12:56:21 +0000242 utils.wait_until_true(lambda: self._check_bw(
243 ssh_client,
244 self.fip['floating_ip_address'],
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800245 port=self.NC_PORT),
Maciej Józefczyk41b80192020-03-03 17:10:57 +0100246 timeout=self.CHECK_TIMEOUT,
Arkady Shtemplerc2b80702020-10-10 15:15:43 +0300247 sleep=1,
248 exception=RuntimeError(
249 'Failed scenario: "Create a QoS policy associated with'
250 ' the network" Actual BW is not as expected!'))
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300251
252 # As admin user update QoS rule
253 self.os_admin.network_client.update_bandwidth_limit_rule(
254 bw_limit_policy_id,
255 rule_id,
256 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 2,
257 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 2)
258
259 # Check that actual BW while downloading file
260 # is as expected (Update BW)
261 utils.wait_until_true(lambda: self._check_bw(
262 ssh_client,
263 self.fip['floating_ip_address'],
264 port=self.NC_PORT,
265 expected_bw=QoSTest.LIMIT_BYTES_SEC * 2),
Maciej Józefczyk41b80192020-03-03 17:10:57 +0100266 timeout=self.CHECK_TIMEOUT,
Arkady Shtemplerc2b80702020-10-10 15:15:43 +0300267 sleep=1,
268 exception=RuntimeError(
269 'Failed scenario: "Update QoS policy associated with'
270 ' the network" Actual BW is not as expected!'))
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300271
272 # Create a new QoS policy
273 bw_limit_policy_id_new = self._create_qos_policy()
274
275 # As admin user create a new QoS rule
276 rule_id_new = self.os_admin.network_client.create_bandwidth_limit_rule(
277 policy_id=bw_limit_policy_id_new,
278 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
279 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)[
280 'bandwidth_limit_rule']['id']
281
282 # Associate a new QoS policy to Neutron port
283 self.os_admin.network_client.update_port(
284 self.port['id'], qos_policy_id=bw_limit_policy_id_new)
285
286 # Check that actual BW while downloading file
287 # is as expected (Original BW)
288 utils.wait_until_true(lambda: self._check_bw(
289 ssh_client,
290 self.fip['floating_ip_address'],
291 port=self.NC_PORT),
Maciej Józefczyk41b80192020-03-03 17:10:57 +0100292 timeout=self.CHECK_TIMEOUT,
Arkady Shtemplerc2b80702020-10-10 15:15:43 +0300293 sleep=1,
294 exception=RuntimeError(
295 'Failed scenario: "Create a new QoS policy associated with'
296 ' the VM port" Actual BW is not as expected!'))
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300297
298 # As admin user update QoS rule
299 self.os_admin.network_client.update_bandwidth_limit_rule(
300 bw_limit_policy_id_new,
301 rule_id_new,
302 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 3,
303 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 3)
304
305 # Check that actual BW while downloading file
306 # is as expected (Update BW)
307 utils.wait_until_true(lambda: self._check_bw(
308 ssh_client,
309 self.fip['floating_ip_address'],
Maciej Józefczyk41b80192020-03-03 17:10:57 +0100310 port=self.NC_PORT,
311 expected_bw=QoSTest.LIMIT_BYTES_SEC * 3),
312 timeout=self.CHECK_TIMEOUT,
Arkady Shtemplerc2b80702020-10-10 15:15:43 +0300313 sleep=1,
314 exception=RuntimeError(
315 'Failed scenario: "Update QoS policy associated with'
316 ' the VM port" Actual BW is not as expected!'))
nfridman55f2ee62019-12-17 03:06:12 -0500317
318 @decorators.idempotent_id('66e5673e-0522-11ea-8d71-362b9e155667')
319 def test_attach_previously_used_port_to_new_instance(self):
320 """The test spawns new instance using port with QoS policy.
321
322        Ports with attached QoS policy could be used multiple times.
323        The policy rules have to be enforced on the new machines.
324 """
325 self.network = self.create_network()
326 self.subnet = self.create_subnet(self.network)
327 self.router = self.create_router_by_client()
328 self.create_router_interface(self.router['id'], self.subnet['id'])
329
330 vm, vm_port = self._create_server_by_port()
331
332 port_policy = self.os_admin.network_client.create_qos_policy(
333 name='port-policy',
334 description='policy for attach',
335 shared=False)['policy']
336
337 rule = self.os_admin.network_client.create_bandwidth_limit_rule(
338 policy_id=port_policy['id'],
339 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
340 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)[
341 'bandwidth_limit_rule']
342
343 self.os_admin.network_client.update_port(
344 vm_port['id'], qos_policy_id=port_policy['id'])
345
346 self.os_primary.servers_client.delete_server(vm['server']['id'])
347 waiters.wait_for_server_termination(
348 self.os_primary.servers_client,
349 vm['server']['id'])
350
351 # Launch a new server using the same port with attached policy
352 self._create_server_by_port(port=vm_port)
353
354 retrieved_port = self.os_admin.network_client.show_port(
355 vm_port['id'])
356 self.assertEqual(port_policy['id'],
357 retrieved_port['port']['qos_policy_id'],
358 """The expected policy ID is {0},
359 the actual value is {1}""".
360 format(port_policy['id'],
361 retrieved_port['port']['qos_policy_id']))
362
363 retrieved_policy = self.os_admin.network_client.show_qos_policy(
364 retrieved_port['port']['qos_policy_id'])
365
366 retrieved_rule_id = retrieved_policy['policy']['rules'][0]['id']
367 self.assertEqual(rule['id'],
368 retrieved_rule_id,
369 """The expected rule ID is {0},
370 the actual value is {1}""".
371 format(rule['id'], retrieved_rule_id))
nfridman5451fab2020-03-12 11:17:43 +0200372
373 @decorators.idempotent_id('4eee64da-5646-11ea-82b4-0242ac130003')
374 def test_create_instance_using_network_with_existing_policy(self):
375 network = self.create_network()
376
377 qos_policy = self.os_admin.network_client.create_qos_policy(
378 name='network-policy',
379 shared=False)['policy']
380
381 rule = self.os_admin.network_client.create_bandwidth_limit_rule(
382 policy_id=qos_policy['id'],
383 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
384 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)
385
386 network = self.os_admin.network_client.update_network(
387 network['id'],
388 qos_policy_id=qos_policy['id'])['network']
389 self.setup_network_and_server(network=network)
390 retrieved_net = self.client.show_network(network['id'])
391 self.assertEqual(qos_policy['id'],
392 retrieved_net['network']['qos_policy_id'],
393 """The expected policy ID is {0},
394 the actual value is {1}""".
395 format(qos_policy['id'],
396 retrieved_net['network']['qos_policy_id']))
397
398 retrieved_policy = self.os_admin.network_client.show_qos_policy(
399 retrieved_net['network']['qos_policy_id'])
400 retrieved_rule_id = retrieved_policy['policy']['rules'][0]['id']
401
402 self.assertEqual(rule['bandwidth_limit_rule']['id'],
403 retrieved_rule_id,
404 """The expected rule ID is {0},
405 the actual value is {1}""".
406 format(rule['bandwidth_limit_rule']['id'],
407 retrieved_rule_id))