blob: 4e0f6bcc4d94e2be1ebc9aaf98d671c1bbff7232 [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
Sławek Kapłońskic0caa2e2017-02-25 10:11:32 +000022from tempest.lib import decorators
Itzik Brown1ef813a2016-06-06 12:56:21 +000023from tempest.lib import exceptions
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ózefczyk27f071f2020-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 @staticmethod
83 def _kill_nc_process(ssh_client):
Itzik Brown1ef813a2016-06-06 12:56:21 +000084 cmd = "killall -q nc"
85 try:
Rodolfo Alonso Hernandezaa65dfb2019-09-18 11:30:04 +000086 ssh_client.exec_command(cmd, timeout=5)
Itzik Brown1ef813a2016-06-06 12:56:21 +000087 except exceptions.SSHExecCommandFailed:
88 pass
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000089
90 def _check_bw(self, ssh_client, host, port, expected_bw=LIMIT_BYTES_SEC):
91 self._kill_nc_process(ssh_client)
Maciej Józefczyk27f071f2020-03-03 17:10:57 +010092 cmd = ("(nc -ll -p %d < /dev/zero > /dev/null &)" % port)
Rodolfo Alonso Hernandezaa65dfb2019-09-18 11:30:04 +000093 ssh_client.exec_command(cmd, timeout=5)
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +010094
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +030095 # Open TCP socket to remote VM and download big file
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +010096 start_time = time.time()
Maciej Józefczyk27f071f2020-03-03 17:10:57 +010097 client_socket = _connect_socket(
98 host, port, constants.SOCKET_CONNECT_TIMEOUT)
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +010099 total_bytes_read = 0
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000100 try:
Maciej Józefczyk27f071f2020-03-03 17:10:57 +0100101 while time.time() - start_time < self.DOWNLOAD_DURATION:
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000102 data = client_socket.recv(self.BUFFER_SIZE)
103 total_bytes_read += len(data)
Itzik Brown1ef813a2016-06-06 12:56:21 +0000104
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000105 # Calculate and return actual BW + logging result
106 time_elapsed = time.time() - start_time
107 bytes_per_second = total_bytes_read / time_elapsed
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +0100108
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000109 LOG.debug("time_elapsed = %(time_elapsed).16f, "
110 "total_bytes_read = %(total_bytes_read)d, "
Maciej Józefczyk27f071f2020-03-03 17:10:57 +0100111 "bytes_per_second = %(bytes_per_second)d, "
112 "expected_bw = %(expected_bw)d.",
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000113 {'time_elapsed': time_elapsed,
114 'total_bytes_read': total_bytes_read,
Maciej Józefczyk27f071f2020-03-03 17:10:57 +0100115 'bytes_per_second': bytes_per_second,
116 'expected_bw': expected_bw})
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000117 return bytes_per_second <= expected_bw
118 except socket.timeout:
119 LOG.warning('Socket timeout while reading the remote file, bytes '
120 'read: %s', total_bytes_read)
Rodolfo Alonso Hernandez31993d52019-08-02 13:42:18 +0000121 self._kill_nc_process(ssh_client)
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000122 return False
123 finally:
124 client_socket.close()
Itzik Brown1ef813a2016-06-06 12:56:21 +0000125
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800126 def _create_ssh_client(self):
127 return ssh.Client(self.fip['floating_ip_address'],
128 CONF.validation.image_ssh_user,
129 pkey=self.keypair['private_key'])
130
131 def _test_basic_resources(self):
132 self.setup_network_and_server()
133 self.check_connectivity(self.fip['floating_ip_address'],
134 CONF.validation.image_ssh_user,
135 self.keypair['private_key'])
136 rulesets = [{'protocol': 'tcp',
137 'direction': 'ingress',
138 'port_range_min': self.NC_PORT,
139 'port_range_max': self.NC_PORT,
140 'remote_ip_prefix': '0.0.0.0/0'}]
141 self.create_secgroup_rules(rulesets,
142 self.security_groups[-1]['id'])
143
144 def _create_qos_policy(self):
145 policy = self.os_admin.network_client.create_qos_policy(
146 name='test-policy',
147 description='test-qos-policy',
148 shared=True)
149 return policy['policy']['id']
150
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900151
152class QoSTest(QoSTestMixin, base.BaseTempestTestCase):
153 @classmethod
154 @tutils.requires_ext(extension="qos", service="network")
155 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
156 def resource_setup(cls):
157 super(QoSTest, cls).resource_setup()
158
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300159 @decorators.idempotent_id('00682a0c-b72e-11e8-b81e-8c16450ea513')
160 def test_qos_basic_and_update(self):
161 """This test covers both:
Itzik Brown1ef813a2016-06-06 12:56:21 +0000162
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300163 1) Basic QoS functionality
164 This is a basic test that check that a QoS policy with
165 a bandwidth limit rule is applied correctly by sending
166 a file from the instance to the test node.
167 Then calculating the bandwidth every ~1 sec by the number of bits
168 received / elapsed time.
169
170 2) Update QoS policy
171 Administrator has the ability to update existing QoS policy,
172 this test is planned to verify that:
173 - actual BW is affected as expected after updating QoS policy.
174 Test scenario:
175 1) Associating QoS Policy with "Original_bandwidth"
176 to the test node
177 2) BW validation - by downloading file on test node.
178 ("Original_bandwidth" is expected)
179 3) Updating existing QoS Policy to a new BW value
180 "Updated_bandwidth"
181 4) BW validation - by downloading file on test node.
182 ("Updated_bandwidth" is expected)
183 Note:
184 There are two options to associate QoS policy to VM:
185 "Neutron Port" or "Network", in this test
186 both options are covered.
Itzik Brown1ef813a2016-06-06 12:56:21 +0000187 """
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300188
189 # Setup resources
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800190 self._test_basic_resources()
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800191 ssh_client = self._create_ssh_client()
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300192
193 # Create QoS policy
194 bw_limit_policy_id = self._create_qos_policy()
195
196 # As admin user create QoS rule
197 rule_id = self.os_admin.network_client.create_bandwidth_limit_rule(
198 policy_id=bw_limit_policy_id,
199 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
200 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)[
201 'bandwidth_limit_rule']['id']
202
203 # Associate QoS to the network
204 self.os_admin.network_client.update_network(
205 self.network['id'], qos_policy_id=bw_limit_policy_id)
206
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300207 # Basic test, Check that actual BW while downloading file
208 # is as expected (Original BW)
Itzik Brown1ef813a2016-06-06 12:56:21 +0000209 utils.wait_until_true(lambda: self._check_bw(
210 ssh_client,
211 self.fip['floating_ip_address'],
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800212 port=self.NC_PORT),
Maciej Józefczyk27f071f2020-03-03 17:10:57 +0100213 timeout=self.CHECK_TIMEOUT,
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300214 sleep=1)
215
216 # As admin user update QoS rule
217 self.os_admin.network_client.update_bandwidth_limit_rule(
218 bw_limit_policy_id,
219 rule_id,
220 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 2,
221 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 2)
222
223 # Check that actual BW while downloading file
224 # is as expected (Update BW)
225 utils.wait_until_true(lambda: self._check_bw(
226 ssh_client,
227 self.fip['floating_ip_address'],
228 port=self.NC_PORT,
229 expected_bw=QoSTest.LIMIT_BYTES_SEC * 2),
Maciej Józefczyk27f071f2020-03-03 17:10:57 +0100230 timeout=self.CHECK_TIMEOUT,
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300231 sleep=1)
232
233 # Create a new QoS policy
234 bw_limit_policy_id_new = self._create_qos_policy()
235
236 # As admin user create a new QoS rule
237 rule_id_new = self.os_admin.network_client.create_bandwidth_limit_rule(
238 policy_id=bw_limit_policy_id_new,
239 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
240 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)[
241 'bandwidth_limit_rule']['id']
242
243 # Associate a new QoS policy to Neutron port
244 self.os_admin.network_client.update_port(
245 self.port['id'], qos_policy_id=bw_limit_policy_id_new)
246
247 # Check that actual BW while downloading file
248 # is as expected (Original BW)
249 utils.wait_until_true(lambda: self._check_bw(
250 ssh_client,
251 self.fip['floating_ip_address'],
252 port=self.NC_PORT),
Maciej Józefczyk27f071f2020-03-03 17:10:57 +0100253 timeout=self.CHECK_TIMEOUT,
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300254 sleep=1)
255
256 # As admin user update QoS rule
257 self.os_admin.network_client.update_bandwidth_limit_rule(
258 bw_limit_policy_id_new,
259 rule_id_new,
260 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 3,
261 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 3)
262
263 # Check that actual BW while downloading file
264 # is as expected (Update BW)
265 utils.wait_until_true(lambda: self._check_bw(
266 ssh_client,
267 self.fip['floating_ip_address'],
Maciej Józefczyk27f071f2020-03-03 17:10:57 +0100268 port=self.NC_PORT,
269 expected_bw=QoSTest.LIMIT_BYTES_SEC * 3),
270 timeout=self.CHECK_TIMEOUT,
Itzik Brown1ef813a2016-06-06 12:56:21 +0000271 sleep=1)