blob: f8f1b035444b5987b69a3a7af1a2169b3c880bea [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 +000023
Chandan Kumar667d3d32017-09-22 12:24:06 +053024from neutron_tempest_plugin.api import base as base_api
Chandan Kumar667d3d32017-09-22 12:24:06 +053025from neutron_tempest_plugin.common import ssh
26from neutron_tempest_plugin.common import utils
27from neutron_tempest_plugin import config
28from neutron_tempest_plugin.scenario import base
29from neutron_tempest_plugin.scenario import constants
30from neutron_tempest_plugin.scenario import exceptions as sc_exceptions
Itzik Brown1ef813a2016-06-06 12:56:21 +000031
32CONF = config.CONF
33LOG = logging.getLogger(__name__)
34
35
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000036def _try_connect(host_ip, port, socket_timeout):
Itzik Brown1ef813a2016-06-06 12:56:21 +000037 try:
38 client_socket = socket.socket(socket.AF_INET,
39 socket.SOCK_STREAM)
40 client_socket.connect((host_ip, port))
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000041 client_socket.settimeout(socket_timeout)
Itzik Brown1ef813a2016-06-06 12:56:21 +000042 return client_socket
43 except socket.error as serr:
44 if serr.errno == errno.ECONNREFUSED:
45 raise sc_exceptions.SocketConnectionRefused(host=host_ip,
46 port=port)
47 else:
48 raise
49
50
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000051def _connect_socket(host, port, socket_timeout):
Brian Haleyaee61ac2018-10-09 20:00:27 -040052 """Try to initiate a connection to a host using an ip address and a port.
Itzik Brown1ef813a2016-06-06 12:56:21 +000053
54 Trying couple of times until a timeout is reached in case the listening
55 host is not ready yet.
56 """
57
58 start = time.time()
59 while True:
60 try:
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000061 return _try_connect(host, port, socket_timeout)
Itzik Brown1ef813a2016-06-06 12:56:21 +000062 except sc_exceptions.SocketConnectionRefused:
63 if time.time() - start > constants.SOCKET_CONNECT_TIMEOUT:
64 raise sc_exceptions.ConnectionTimeoutException(host=host,
65 port=port)
66
67
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +090068class QoSTestMixin(object):
Itzik Brown1ef813a2016-06-06 12:56:21 +000069 credentials = ['primary', 'admin']
70 force_tenant_isolation = False
71
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000072 FILE_SIZE = 1024 * 1024
Itzik Brown1ef813a2016-06-06 12:56:21 +000073 TOLERANCE_FACTOR = 1.5
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000074 BUFFER_SIZE = 512
75 COUNT = FILE_SIZE / BUFFER_SIZE
Brian Haley33ef4602018-04-26 14:37:49 -040076 LIMIT_BYTES_SEC = (constants.LIMIT_KILO_BITS_PER_SECOND * 1024 *
77 TOLERANCE_FACTOR / 8.0)
Itzik Brown1ef813a2016-06-06 12:56:21 +000078 FILE_PATH = "/tmp/img"
79
LIU Yulong5ba88ef2017-12-22 10:50:15 +080080 NC_PORT = 1234
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +030081 FILE_DOWNLOAD_TIMEOUT = 120
LIU Yulong5ba88ef2017-12-22 10:50:15 +080082
Itzik Brown1ef813a2016-06-06 12:56:21 +000083 def _create_file_for_bw_tests(self, ssh_client):
84 cmd = ("(dd if=/dev/zero bs=%(bs)d count=%(count)d of=%(file_path)s) "
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000085 % {'bs': self.BUFFER_SIZE, 'count': self.COUNT,
86 'file_path': self.FILE_PATH})
Rodolfo Alonso Hernandezaa65dfb2019-09-18 11:30:04 +000087 ssh_client.exec_command(cmd, timeout=5)
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000088 cmd = "stat -c %%s %s" % self.FILE_PATH
Rodolfo Alonso Hernandezaa65dfb2019-09-18 11:30:04 +000089 filesize = ssh_client.exec_command(cmd, timeout=5)
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000090 if int(filesize.strip()) != self.FILE_SIZE:
Itzik Brown1ef813a2016-06-06 12:56:21 +000091 raise sc_exceptions.FileCreationFailedException(
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000092 file=self.FILE_PATH)
Itzik Brown1ef813a2016-06-06 12:56:21 +000093
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000094 def _check_bw(self, ssh_client, host, port, expected_bw=LIMIT_BYTES_SEC):
Maciej Józefczyk328edc82019-09-16 14:05:48 +000095 utils.kill_nc_process(ssh_client)
Itzik Brown1ef813a2016-06-06 12:56:21 +000096 cmd = ("(nc -ll -p %(port)d < %(file_path)s > /dev/null &)" % {
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000097 'port': port, 'file_path': self.FILE_PATH})
Rodolfo Alonso Hernandezaa65dfb2019-09-18 11:30:04 +000098 ssh_client.exec_command(cmd, timeout=5)
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +010099
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300100 # Open TCP socket to remote VM and download big file
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +0100101 start_time = time.time()
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000102 socket_timeout = self.FILE_SIZE * self.TOLERANCE_FACTOR / expected_bw
103 client_socket = _connect_socket(host, port, socket_timeout)
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +0100104 total_bytes_read = 0
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000105 try:
106 while total_bytes_read < self.FILE_SIZE:
107 data = client_socket.recv(self.BUFFER_SIZE)
108 total_bytes_read += len(data)
Itzik Brown1ef813a2016-06-06 12:56:21 +0000109
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000110 # Calculate and return actual BW + logging result
111 time_elapsed = time.time() - start_time
112 bytes_per_second = total_bytes_read / time_elapsed
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +0100113
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000114 LOG.debug("time_elapsed = %(time_elapsed).16f, "
115 "total_bytes_read = %(total_bytes_read)d, "
116 "bytes_per_second = %(bytes_per_second)d",
117 {'time_elapsed': time_elapsed,
118 'total_bytes_read': total_bytes_read,
119 'bytes_per_second': bytes_per_second})
120 return bytes_per_second <= expected_bw
121 except socket.timeout:
122 LOG.warning('Socket timeout while reading the remote file, bytes '
123 'read: %s', total_bytes_read)
Maciej Józefczyk328edc82019-09-16 14:05:48 +0000124 utils.kill_nc_process(ssh_client)
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000125 return False
126 finally:
127 client_socket.close()
Itzik Brown1ef813a2016-06-06 12:56:21 +0000128
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800129 def _create_ssh_client(self):
130 return ssh.Client(self.fip['floating_ip_address'],
131 CONF.validation.image_ssh_user,
132 pkey=self.keypair['private_key'])
133
134 def _test_basic_resources(self):
135 self.setup_network_and_server()
136 self.check_connectivity(self.fip['floating_ip_address'],
137 CONF.validation.image_ssh_user,
138 self.keypair['private_key'])
139 rulesets = [{'protocol': 'tcp',
140 'direction': 'ingress',
141 'port_range_min': self.NC_PORT,
142 'port_range_max': self.NC_PORT,
143 'remote_ip_prefix': '0.0.0.0/0'}]
144 self.create_secgroup_rules(rulesets,
145 self.security_groups[-1]['id'])
146
147 def _create_qos_policy(self):
148 policy = self.os_admin.network_client.create_qos_policy(
149 name='test-policy',
150 description='test-qos-policy',
151 shared=True)
152 return policy['policy']['id']
153
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900154
155class QoSTest(QoSTestMixin, base.BaseTempestTestCase):
156 @classmethod
157 @tutils.requires_ext(extension="qos", service="network")
158 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
159 def resource_setup(cls):
160 super(QoSTest, cls).resource_setup()
161
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300162 @decorators.idempotent_id('00682a0c-b72e-11e8-b81e-8c16450ea513')
163 def test_qos_basic_and_update(self):
164 """This test covers both:
Itzik Brown1ef813a2016-06-06 12:56:21 +0000165
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300166 1) Basic QoS functionality
167 This is a basic test that check that a QoS policy with
168 a bandwidth limit rule is applied correctly by sending
169 a file from the instance to the test node.
170 Then calculating the bandwidth every ~1 sec by the number of bits
171 received / elapsed time.
172
173 2) Update QoS policy
174 Administrator has the ability to update existing QoS policy,
175 this test is planned to verify that:
176 - actual BW is affected as expected after updating QoS policy.
177 Test scenario:
178 1) Associating QoS Policy with "Original_bandwidth"
179 to the test node
180 2) BW validation - by downloading file on test node.
181 ("Original_bandwidth" is expected)
182 3) Updating existing QoS Policy to a new BW value
183 "Updated_bandwidth"
184 4) BW validation - by downloading file on test node.
185 ("Updated_bandwidth" is expected)
186 Note:
187 There are two options to associate QoS policy to VM:
188 "Neutron Port" or "Network", in this test
189 both options are covered.
Itzik Brown1ef813a2016-06-06 12:56:21 +0000190 """
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300191
192 # Setup resources
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800193 self._test_basic_resources()
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800194 ssh_client = self._create_ssh_client()
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300195
196 # Create QoS policy
197 bw_limit_policy_id = self._create_qos_policy()
198
199 # As admin user create QoS rule
200 rule_id = self.os_admin.network_client.create_bandwidth_limit_rule(
201 policy_id=bw_limit_policy_id,
202 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
203 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)[
204 'bandwidth_limit_rule']['id']
205
206 # Associate QoS to the network
207 self.os_admin.network_client.update_network(
208 self.network['id'], qos_policy_id=bw_limit_policy_id)
209
210 # Create file on VM
Itzik Brown1ef813a2016-06-06 12:56:21 +0000211 self._create_file_for_bw_tests(ssh_client)
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300212
213 # Basic test, Check that actual BW while downloading file
214 # is as expected (Original BW)
Itzik Brown1ef813a2016-06-06 12:56:21 +0000215 utils.wait_until_true(lambda: self._check_bw(
216 ssh_client,
217 self.fip['floating_ip_address'],
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800218 port=self.NC_PORT),
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300219 timeout=self.FILE_DOWNLOAD_TIMEOUT,
220 sleep=1)
221
222 # As admin user update QoS rule
223 self.os_admin.network_client.update_bandwidth_limit_rule(
224 bw_limit_policy_id,
225 rule_id,
226 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 2,
227 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 2)
228
229 # Check that actual BW while downloading file
230 # is as expected (Update BW)
231 utils.wait_until_true(lambda: self._check_bw(
232 ssh_client,
233 self.fip['floating_ip_address'],
234 port=self.NC_PORT,
235 expected_bw=QoSTest.LIMIT_BYTES_SEC * 2),
236 timeout=self.FILE_DOWNLOAD_TIMEOUT,
237 sleep=1)
238
239 # Create a new QoS policy
240 bw_limit_policy_id_new = self._create_qos_policy()
241
242 # As admin user create a new QoS rule
243 rule_id_new = self.os_admin.network_client.create_bandwidth_limit_rule(
244 policy_id=bw_limit_policy_id_new,
245 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
246 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)[
247 'bandwidth_limit_rule']['id']
248
249 # Associate a new QoS policy to Neutron port
250 self.os_admin.network_client.update_port(
251 self.port['id'], qos_policy_id=bw_limit_policy_id_new)
252
253 # Check that actual BW while downloading file
254 # is as expected (Original BW)
255 utils.wait_until_true(lambda: self._check_bw(
256 ssh_client,
257 self.fip['floating_ip_address'],
258 port=self.NC_PORT),
259 timeout=self.FILE_DOWNLOAD_TIMEOUT,
260 sleep=1)
261
262 # As admin user update QoS rule
263 self.os_admin.network_client.update_bandwidth_limit_rule(
264 bw_limit_policy_id_new,
265 rule_id_new,
266 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 3,
267 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 3)
268
269 # Check that actual BW while downloading file
270 # is as expected (Update BW)
271 utils.wait_until_true(lambda: self._check_bw(
272 ssh_client,
273 self.fip['floating_ip_address'],
274 port=self.NC_PORT, expected_bw=QoSTest.LIMIT_BYTES_SEC * 3),
275 timeout=self.FILE_DOWNLOAD_TIMEOUT,
Itzik Brown1ef813a2016-06-06 12:56:21 +0000276 sleep=1)