blob: 13a06aeb273c123157c1a6455f1972e647f718c4 [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
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000073 FILE_SIZE = 1024 * 1024
Itzik Brown1ef813a2016-06-06 12:56:21 +000074 TOLERANCE_FACTOR = 1.5
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000075 BUFFER_SIZE = 512
76 COUNT = FILE_SIZE / BUFFER_SIZE
Brian Haley33ef4602018-04-26 14:37:49 -040077 LIMIT_BYTES_SEC = (constants.LIMIT_KILO_BITS_PER_SECOND * 1024 *
78 TOLERANCE_FACTOR / 8.0)
Itzik Brown1ef813a2016-06-06 12:56:21 +000079 FILE_PATH = "/tmp/img"
80
LIU Yulong5ba88ef2017-12-22 10:50:15 +080081 NC_PORT = 1234
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +030082 FILE_DOWNLOAD_TIMEOUT = 120
LIU Yulong5ba88ef2017-12-22 10:50:15 +080083
Itzik Brown1ef813a2016-06-06 12:56:21 +000084 def _create_file_for_bw_tests(self, ssh_client):
85 cmd = ("(dd if=/dev/zero bs=%(bs)d count=%(count)d of=%(file_path)s) "
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000086 % {'bs': self.BUFFER_SIZE, 'count': self.COUNT,
87 'file_path': self.FILE_PATH})
Itzik Brown1ef813a2016-06-06 12:56:21 +000088 ssh_client.exec_command(cmd)
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000089 cmd = "stat -c %%s %s" % self.FILE_PATH
Itzik Brown1ef813a2016-06-06 12:56:21 +000090 filesize = ssh_client.exec_command(cmd)
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000091 if int(filesize.strip()) != self.FILE_SIZE:
Itzik Brown1ef813a2016-06-06 12:56:21 +000092 raise sc_exceptions.FileCreationFailedException(
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000093 file=self.FILE_PATH)
Itzik Brown1ef813a2016-06-06 12:56:21 +000094
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000095 @staticmethod
96 def _kill_nc_process(ssh_client):
Itzik Brown1ef813a2016-06-06 12:56:21 +000097 cmd = "killall -q nc"
98 try:
99 ssh_client.exec_command(cmd)
100 except exceptions.SSHExecCommandFailed:
101 pass
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000102
103 def _check_bw(self, ssh_client, host, port, expected_bw=LIMIT_BYTES_SEC):
104 self._kill_nc_process(ssh_client)
Itzik Brown1ef813a2016-06-06 12:56:21 +0000105 cmd = ("(nc -ll -p %(port)d < %(file_path)s > /dev/null &)" % {
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000106 'port': port, 'file_path': self.FILE_PATH})
Itzik Brown1ef813a2016-06-06 12:56:21 +0000107 ssh_client.exec_command(cmd)
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +0100108
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300109 # Open TCP socket to remote VM and download big file
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +0100110 start_time = time.time()
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000111 socket_timeout = self.FILE_SIZE * self.TOLERANCE_FACTOR / expected_bw
112 client_socket = _connect_socket(host, port, socket_timeout)
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +0100113 total_bytes_read = 0
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000114 try:
115 while total_bytes_read < self.FILE_SIZE:
116 data = client_socket.recv(self.BUFFER_SIZE)
117 total_bytes_read += len(data)
Itzik Brown1ef813a2016-06-06 12:56:21 +0000118
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000119 # Calculate and return actual BW + logging result
120 time_elapsed = time.time() - start_time
121 bytes_per_second = total_bytes_read / time_elapsed
Miguel Angel Ajod47e21a2017-02-07 16:21:16 +0100122
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +0000123 LOG.debug("time_elapsed = %(time_elapsed).16f, "
124 "total_bytes_read = %(total_bytes_read)d, "
125 "bytes_per_second = %(bytes_per_second)d",
126 {'time_elapsed': time_elapsed,
127 'total_bytes_read': total_bytes_read,
128 'bytes_per_second': bytes_per_second})
129 return bytes_per_second <= expected_bw
130 except socket.timeout:
131 LOG.warning('Socket timeout while reading the remote file, bytes '
132 'read: %s', total_bytes_read)
133 return False
134 finally:
135 client_socket.close()
136 self._kill_nc_process(ssh_client)
Itzik Brown1ef813a2016-06-06 12:56:21 +0000137
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800138 def _create_ssh_client(self):
139 return ssh.Client(self.fip['floating_ip_address'],
140 CONF.validation.image_ssh_user,
141 pkey=self.keypair['private_key'])
142
143 def _test_basic_resources(self):
144 self.setup_network_and_server()
145 self.check_connectivity(self.fip['floating_ip_address'],
146 CONF.validation.image_ssh_user,
147 self.keypair['private_key'])
148 rulesets = [{'protocol': 'tcp',
149 'direction': 'ingress',
150 'port_range_min': self.NC_PORT,
151 'port_range_max': self.NC_PORT,
152 'remote_ip_prefix': '0.0.0.0/0'}]
153 self.create_secgroup_rules(rulesets,
154 self.security_groups[-1]['id'])
155
156 def _create_qos_policy(self):
157 policy = self.os_admin.network_client.create_qos_policy(
158 name='test-policy',
159 description='test-qos-policy',
160 shared=True)
161 return policy['policy']['id']
162
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900163
164class QoSTest(QoSTestMixin, base.BaseTempestTestCase):
165 @classmethod
166 @tutils.requires_ext(extension="qos", service="network")
167 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
168 def resource_setup(cls):
169 super(QoSTest, cls).resource_setup()
170
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300171 @decorators.idempotent_id('00682a0c-b72e-11e8-b81e-8c16450ea513')
172 def test_qos_basic_and_update(self):
173 """This test covers both:
Itzik Brown1ef813a2016-06-06 12:56:21 +0000174
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300175 1) Basic QoS functionality
176 This is a basic test that check that a QoS policy with
177 a bandwidth limit rule is applied correctly by sending
178 a file from the instance to the test node.
179 Then calculating the bandwidth every ~1 sec by the number of bits
180 received / elapsed time.
181
182 2) Update QoS policy
183 Administrator has the ability to update existing QoS policy,
184 this test is planned to verify that:
185 - actual BW is affected as expected after updating QoS policy.
186 Test scenario:
187 1) Associating QoS Policy with "Original_bandwidth"
188 to the test node
189 2) BW validation - by downloading file on test node.
190 ("Original_bandwidth" is expected)
191 3) Updating existing QoS Policy to a new BW value
192 "Updated_bandwidth"
193 4) BW validation - by downloading file on test node.
194 ("Updated_bandwidth" is expected)
195 Note:
196 There are two options to associate QoS policy to VM:
197 "Neutron Port" or "Network", in this test
198 both options are covered.
Itzik Brown1ef813a2016-06-06 12:56:21 +0000199 """
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300200
201 # Setup resources
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800202 self._test_basic_resources()
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800203 ssh_client = self._create_ssh_client()
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300204
205 # Create QoS policy
206 bw_limit_policy_id = self._create_qos_policy()
207
208 # As admin user create QoS rule
209 rule_id = self.os_admin.network_client.create_bandwidth_limit_rule(
210 policy_id=bw_limit_policy_id,
211 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
212 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)[
213 'bandwidth_limit_rule']['id']
214
215 # Associate QoS to the network
216 self.os_admin.network_client.update_network(
217 self.network['id'], qos_policy_id=bw_limit_policy_id)
218
219 # Create file on VM
Itzik Brown1ef813a2016-06-06 12:56:21 +0000220 self._create_file_for_bw_tests(ssh_client)
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300221
222 # Basic test, Check that actual BW while downloading file
223 # is as expected (Original BW)
Itzik Brown1ef813a2016-06-06 12:56:21 +0000224 utils.wait_until_true(lambda: self._check_bw(
225 ssh_client,
226 self.fip['floating_ip_address'],
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800227 port=self.NC_PORT),
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300228 timeout=self.FILE_DOWNLOAD_TIMEOUT,
229 sleep=1)
230
231 # As admin user update QoS rule
232 self.os_admin.network_client.update_bandwidth_limit_rule(
233 bw_limit_policy_id,
234 rule_id,
235 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 2,
236 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 2)
237
238 # Check that actual BW while downloading file
239 # is as expected (Update BW)
240 utils.wait_until_true(lambda: self._check_bw(
241 ssh_client,
242 self.fip['floating_ip_address'],
243 port=self.NC_PORT,
244 expected_bw=QoSTest.LIMIT_BYTES_SEC * 2),
245 timeout=self.FILE_DOWNLOAD_TIMEOUT,
246 sleep=1)
247
248 # Create a new QoS policy
249 bw_limit_policy_id_new = self._create_qos_policy()
250
251 # As admin user create a new QoS rule
252 rule_id_new = self.os_admin.network_client.create_bandwidth_limit_rule(
253 policy_id=bw_limit_policy_id_new,
254 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
255 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)[
256 'bandwidth_limit_rule']['id']
257
258 # Associate a new QoS policy to Neutron port
259 self.os_admin.network_client.update_port(
260 self.port['id'], qos_policy_id=bw_limit_policy_id_new)
261
262 # Check that actual BW while downloading file
263 # is as expected (Original BW)
264 utils.wait_until_true(lambda: self._check_bw(
265 ssh_client,
266 self.fip['floating_ip_address'],
267 port=self.NC_PORT),
268 timeout=self.FILE_DOWNLOAD_TIMEOUT,
269 sleep=1)
270
271 # As admin user update QoS rule
272 self.os_admin.network_client.update_bandwidth_limit_rule(
273 bw_limit_policy_id_new,
274 rule_id_new,
275 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 3,
276 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 3)
277
278 # Check that actual BW while downloading file
279 # is as expected (Update BW)
280 utils.wait_until_true(lambda: self._check_bw(
281 ssh_client,
282 self.fip['floating_ip_address'],
283 port=self.NC_PORT, expected_bw=QoSTest.LIMIT_BYTES_SEC * 3),
284 timeout=self.FILE_DOWNLOAD_TIMEOUT,
Itzik Brown1ef813a2016-06-06 12:56:21 +0000285 sleep=1)