blob: 4a30d091c0b187dd8ac6fb5570228ab7797089b1 [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
19from oslo_log import log as logging
20from tempest.lib.common import ssh
21from tempest.lib import exceptions
22from tempest import test
23
24from neutron.common import utils
25from neutron.tests.tempest import config
26from neutron.tests.tempest.scenario import base
27from neutron.tests.tempest.scenario import constants
28from neutron.tests.tempest.scenario import exceptions as sc_exceptions
29
30CONF = config.CONF
31LOG = logging.getLogger(__name__)
32
33
34def _try_connect(host_ip, port):
35 try:
36 client_socket = socket.socket(socket.AF_INET,
37 socket.SOCK_STREAM)
38 client_socket.connect((host_ip, port))
39 client_socket.setblocking(0)
40 return client_socket
41 except socket.error as serr:
42 if serr.errno == errno.ECONNREFUSED:
43 raise sc_exceptions.SocketConnectionRefused(host=host_ip,
44 port=port)
45 else:
46 raise
47
48
49def _connect_socket(host, port):
50 """Try to initiate a connection to a host using an ip address
51 and a port.
52
53 Trying couple of times until a timeout is reached in case the listening
54 host is not ready yet.
55 """
56
57 start = time.time()
58 while True:
59 try:
60 return _try_connect(host, port)
61 except sc_exceptions.SocketConnectionRefused:
62 if time.time() - start > constants.SOCKET_CONNECT_TIMEOUT:
63 raise sc_exceptions.ConnectionTimeoutException(host=host,
64 port=port)
65
66
67class QoSTest(base.BaseTempestTestCase):
68 credentials = ['primary', 'admin']
69 force_tenant_isolation = False
70
71 BUFFER_SIZE = 1024 * 1024
72 TOLERANCE_FACTOR = 1.5
73 BS = 512
74 COUNT = BUFFER_SIZE / BS
75 FILE_SIZE = BS * COUNT
76 LIMIT_BYTES_SEC = (constants.LIMIT_KILO_BITS_PER_SECOND * 1024
77 * TOLERANCE_FACTOR / 8.0)
78 FILE_PATH = "/tmp/img"
79
80 def _create_file_for_bw_tests(self, ssh_client):
81 cmd = ("(dd if=/dev/zero bs=%(bs)d count=%(count)d of=%(file_path)s) "
82 % {'bs': QoSTest.BS, 'count': QoSTest.COUNT,
83 'file_path': QoSTest.FILE_PATH})
84 ssh_client.exec_command(cmd)
85 cmd = "stat -c %%s %s" % QoSTest.FILE_PATH
86 filesize = ssh_client.exec_command(cmd)
87 if int(filesize.strip()) != QoSTest.FILE_SIZE:
88 raise sc_exceptions.FileCreationFailedException(
89 file=QoSTest.FILE_PATH)
90
91 def _check_bw(self, ssh_client, host, port):
92 total_bytes_read = 0
93 cycle_start_time = time.time()
94 cycle_data_read = 0
95
96 cmd = "killall -q nc"
97 try:
98 ssh_client.exec_command(cmd)
99 except exceptions.SSHExecCommandFailed:
100 pass
101 cmd = ("(nc -ll -p %(port)d < %(file_path)s > /dev/null &)" % {
102 'port': port, 'file_path': QoSTest.FILE_PATH})
103 ssh_client.exec_command(cmd)
104 client_socket = _connect_socket(host, port)
105
106 while total_bytes_read < QoSTest.FILE_SIZE:
107 try:
108 data = client_socket.recv(QoSTest.BUFFER_SIZE)
109 except socket.error as e:
110 if e.args[0] in [errno.EAGAIN, errno.EWOULDBLOCK]:
111 continue
112 else:
113 raise
114 total_bytes_read += len(data)
115 cycle_data_read += len(data)
116 time_elapsed = time.time() - cycle_start_time
117 should_check = (time_elapsed >= 5 or
118 total_bytes_read == QoSTest.FILE_SIZE)
119 if should_check:
120 LOG.debug("time_elapsed = %(time_elapsed)d,"
121 "total_bytes_read = %(bytes_read)d,"
122 "cycle_data_read = %(cycle_data)d",
123 {"time_elapsed": time_elapsed,
124 "bytes_read": total_bytes_read,
125 "cycle_data": cycle_data_read})
126
127 if cycle_data_read / time_elapsed > QoSTest.LIMIT_BYTES_SEC:
128 # Limit reached
129 return False
130 else:
131 cycle_start_time = time.time()
132 cycle_data_read = 0
133 return True
134
135 @test.idempotent_id('1f7ed39b-428f-410a-bd2b-db9f465680df')
136 def test_qos(self):
137 """This is a basic test that check that a QoS policy with
138
139 a bandwidth limit rule is applied correctly by sending
140 a file from the instance to the test node.
141 Then calculating the bandwidth every ~1 sec by the number of bits
142 received / elapsed time.
143 """
144
145 NC_PORT = 1234
146
147 self.setup_network_and_server()
148 self.check_connectivity(self.fip['floating_ip_address'],
149 CONF.validation.image_ssh_user,
150 self.keypair['private_key'])
151 rulesets = [{'protocol': 'tcp',
152 'direction': 'ingress',
153 'port_range_min': NC_PORT,
154 'port_range_max': NC_PORT,
155 'remote_ip_prefix': '0.0.0.0/0'}]
156 self.create_secgroup_rules(rulesets)
157 ssh_client = ssh.Client(self.fip['floating_ip_address'],
158 CONF.validation.image_ssh_user,
159 pkey=self.keypair['private_key'])
160 policy = self.admin_manager.network_client.create_qos_policy(
161 name='test-policy',
162 description='test-qos-policy',
163 shared=True)
164 policy_id = policy['policy']['id']
165 self.admin_manager.network_client.create_bandwidth_limit_rule(
166 policy_id, max_kbps=constants.LIMIT_KILO_BITS_PER_SECOND,
167 max_burst_kbps=constants.LIMIT_KILO_BITS_PER_SECOND)
168 port = self.client.list_ports(network_id=self.network['id'],
169 device_id=self.server[
170 'server']['id'])['ports'][0]
171 self.admin_manager.network_client.update_port(port['id'],
172 qos_policy_id=policy_id)
173 self._create_file_for_bw_tests(ssh_client)
174 utils.wait_until_true(lambda: self._check_bw(
175 ssh_client,
176 self.fip['floating_ip_address'],
177 port=NC_PORT),
178 timeout=120,
179 sleep=1)