blob: c847077b091863f807204f9a7e77869fdd18fb62 [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
Itzik Brown1ef813a2016-06-06 12:56:21 +000072 TOLERANCE_FACTOR = 1.5
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000073 BUFFER_SIZE = 512
Brian Haley33ef4602018-04-26 14:37:49 -040074 LIMIT_BYTES_SEC = (constants.LIMIT_KILO_BITS_PER_SECOND * 1024 *
75 TOLERANCE_FACTOR / 8.0)
LIU Yulong5ba88ef2017-12-22 10:50:15 +080076 NC_PORT = 1234
Maciej Józefczyk41b80192020-03-03 17:10:57 +010077 DOWNLOAD_DURATION = 5
78 # NOTE(mjozefcz): This makes around 10 retries.
79 CHECK_TIMEOUT = DOWNLOAD_DURATION * 10
Itzik Brown1ef813a2016-06-06 12:56:21 +000080
Rodolfo Alonso Hernandezfa5ebc42019-07-26 17:02:40 +000081 def _check_bw(self, ssh_client, host, port, expected_bw=LIMIT_BYTES_SEC):
Maciej Józefczyk328edc82019-09-16 14:05:48 +000082 utils.kill_nc_process(ssh_client)
Maciej Józefczyk41b80192020-03-03 17:10:57 +010083 cmd = ("(nc -ll -p %d < /dev/zero > /dev/null &)" % port)
Rodolfo Alonso Hernandezaa65dfb2019-09-18 11:30:04 +000084 ssh_client.exec_command(cmd, timeout=5)
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)
140 return policy['policy']['id']
141
YAMAMOTO Takashia2cc2e52018-07-31 18:54:02 +0900142
143class QoSTest(QoSTestMixin, base.BaseTempestTestCase):
144 @classmethod
145 @tutils.requires_ext(extension="qos", service="network")
146 @base_api.require_qos_rule_type(qos_consts.RULE_TYPE_BANDWIDTH_LIMIT)
147 def resource_setup(cls):
148 super(QoSTest, cls).resource_setup()
149
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300150 @decorators.idempotent_id('00682a0c-b72e-11e8-b81e-8c16450ea513')
151 def test_qos_basic_and_update(self):
152 """This test covers both:
Itzik Brown1ef813a2016-06-06 12:56:21 +0000153
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300154 1) Basic QoS functionality
155 This is a basic test that check that a QoS policy with
156 a bandwidth limit rule is applied correctly by sending
157 a file from the instance to the test node.
158 Then calculating the bandwidth every ~1 sec by the number of bits
159 received / elapsed time.
160
161 2) Update QoS policy
162 Administrator has the ability to update existing QoS policy,
163 this test is planned to verify that:
164 - actual BW is affected as expected after updating QoS policy.
165 Test scenario:
166 1) Associating QoS Policy with "Original_bandwidth"
167 to the test node
168 2) BW validation - by downloading file on test node.
169 ("Original_bandwidth" is expected)
170 3) Updating existing QoS Policy to a new BW value
171 "Updated_bandwidth"
172 4) BW validation - by downloading file on test node.
173 ("Updated_bandwidth" is expected)
174 Note:
175 There are two options to associate QoS policy to VM:
176 "Neutron Port" or "Network", in this test
177 both options are covered.
Itzik Brown1ef813a2016-06-06 12:56:21 +0000178 """
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300179
180 # Setup resources
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800181 self._test_basic_resources()
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800182 ssh_client = self._create_ssh_client()
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300183
184 # Create QoS policy
185 bw_limit_policy_id = self._create_qos_policy()
186
187 # As admin user create QoS rule
188 rule_id = self.os_admin.network_client.create_bandwidth_limit_rule(
189 policy_id=bw_limit_policy_id,
190 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
191 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)[
192 'bandwidth_limit_rule']['id']
193
194 # Associate QoS to the network
195 self.os_admin.network_client.update_network(
196 self.network['id'], qos_policy_id=bw_limit_policy_id)
197
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300198 # Basic test, Check that actual BW while downloading file
199 # is as expected (Original BW)
Itzik Brown1ef813a2016-06-06 12:56:21 +0000200 utils.wait_until_true(lambda: self._check_bw(
201 ssh_client,
202 self.fip['floating_ip_address'],
LIU Yulong5ba88ef2017-12-22 10:50:15 +0800203 port=self.NC_PORT),
Maciej Józefczyk41b80192020-03-03 17:10:57 +0100204 timeout=self.CHECK_TIMEOUT,
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300205 sleep=1)
206
207 # As admin user update QoS rule
208 self.os_admin.network_client.update_bandwidth_limit_rule(
209 bw_limit_policy_id,
210 rule_id,
211 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 2,
212 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 2)
213
214 # Check that actual BW while downloading file
215 # is as expected (Update BW)
216 utils.wait_until_true(lambda: self._check_bw(
217 ssh_client,
218 self.fip['floating_ip_address'],
219 port=self.NC_PORT,
220 expected_bw=QoSTest.LIMIT_BYTES_SEC * 2),
Maciej Józefczyk41b80192020-03-03 17:10:57 +0100221 timeout=self.CHECK_TIMEOUT,
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300222 sleep=1)
223
224 # Create a new QoS policy
225 bw_limit_policy_id_new = self._create_qos_policy()
226
227 # As admin user create a new QoS rule
228 rule_id_new = self.os_admin.network_client.create_bandwidth_limit_rule(
229 policy_id=bw_limit_policy_id_new,
230 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
231 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)[
232 'bandwidth_limit_rule']['id']
233
234 # Associate a new QoS policy to Neutron port
235 self.os_admin.network_client.update_port(
236 self.port['id'], qos_policy_id=bw_limit_policy_id_new)
237
238 # Check that actual BW while downloading file
239 # is as expected (Original BW)
240 utils.wait_until_true(lambda: self._check_bw(
241 ssh_client,
242 self.fip['floating_ip_address'],
243 port=self.NC_PORT),
Maciej Józefczyk41b80192020-03-03 17:10:57 +0100244 timeout=self.CHECK_TIMEOUT,
Arkady Shtempler3e1d8f12018-08-19 10:36:24 +0300245 sleep=1)
246
247 # As admin user update QoS rule
248 self.os_admin.network_client.update_bandwidth_limit_rule(
249 bw_limit_policy_id_new,
250 rule_id_new,
251 max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 3,
252 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND * 3)
253
254 # Check that actual BW while downloading file
255 # is as expected (Update BW)
256 utils.wait_until_true(lambda: self._check_bw(
257 ssh_client,
258 self.fip['floating_ip_address'],
Maciej Józefczyk41b80192020-03-03 17:10:57 +0100259 port=self.NC_PORT,
260 expected_bw=QoSTest.LIMIT_BYTES_SEC * 3),
261 timeout=self.CHECK_TIMEOUT,
Itzik Brown1ef813a2016-06-06 12:56:21 +0000262 sleep=1)