Merge "Sync versionutils from oslo-incubator"
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index 9f2f924..9051310 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -327,6 +327,11 @@
# (integer value)
#shelved_offload_time=0
+# Unallocated floating IP range, which will be used to test
+# the floating IP bulk feature for CRUD operation. (string
+# value)
+#floating_ip_range=10.0.0.0/29
+
# Allows test cases to create/destroy tenants and users. This
# option enables isolated test cases and better parallel
# execution, but also requires that OpenStack Identity API
@@ -366,17 +371,19 @@
#
# If false, skip all nova v3 tests. (boolean value)
-#api_v3=true
+#api_v3=false
# If false, skip disk config tests (boolean value)
#disk_config=true
# A list of enabled compute extensions with a special entry
-# all which indicates every extension is enabled (list value)
+# all which indicates every extension is enabled. Each
+# extension should be specified with alias name (list value)
#api_extensions=all
# A list of enabled v3 extensions with a special entry all
-# which indicates every extension is enabled (list value)
+# which indicates every extension is enabled. Each extension
+# should be specified with alias name (list value)
#api_v3_extensions=all
# Does the test environment support changing the admin
diff --git a/tempest/api/baremetal/base.py b/tempest/api/baremetal/base.py
index 021adaf..6f7e438 100644
--- a/tempest/api/baremetal/base.py
+++ b/tempest/api/baremetal/base.py
@@ -27,13 +27,12 @@
def decorator(f):
@functools.wraps(f)
def wrapper(cls, *args, **kwargs):
- result = f(cls, *args, **kwargs)
- body = result[resource]
+ resp, body = f(cls, *args, **kwargs)
if 'uuid' in body:
cls.created_objects[resource].add(body['uuid'])
- return result
+ return resp, body
return wrapper
return decorator
@@ -51,7 +50,7 @@
mgr = clients.AdminManager()
cls.client = mgr.baremetal_client
-
+ cls.power_timeout = CONF.baremetal.power_timeout
cls.created_objects = {'chassis': set(),
'port': set(),
'node': set()}
@@ -81,8 +80,7 @@
"""
description = description or data_utils.rand_name('test-chassis-')
resp, body = cls.client.create_chassis(description=description)
-
- return {'chassis': body, 'response': resp}
+ return resp, body
@classmethod
@creates('node')
@@ -102,7 +100,7 @@
cpu_num=cpu_num, storage=storage,
memory=memory, driver=driver)
- return {'node': body, 'response': resp}
+ return resp, body
@classmethod
@creates('port')
@@ -121,7 +119,7 @@
resp, body = cls.client.create_port(address=address, node_id=node_id,
extra=extra, uuid=uuid)
- return {'port': body, 'response': resp}
+ return resp, body
@classmethod
def delete_chassis(cls, chassis_id):
diff --git a/tempest/api/baremetal/test_api_discovery.py b/tempest/api/baremetal/test_api_discovery.py
index e594b3e..bee10b9 100644
--- a/tempest/api/baremetal/test_api_discovery.py
+++ b/tempest/api/baremetal/test_api_discovery.py
@@ -20,6 +20,7 @@
@test.attr(type='smoke')
def test_api_versions(self):
resp, descr = self.client.get_api_description()
+ self.assertEqual('200', resp['status'])
expected_versions = ('v1',)
versions = [version['id'] for version in descr['versions']]
@@ -30,6 +31,7 @@
@test.attr(type='smoke')
def test_default_version(self):
resp, descr = self.client.get_api_description()
+ self.assertEqual('200', resp['status'])
default_version = descr['default_version']
self.assertEqual(default_version['id'], 'v1')
@@ -37,6 +39,7 @@
@test.attr(type='smoke')
def test_version_1_resources(self):
resp, descr = self.client.get_version_description(version='v1')
+ self.assertEqual('200', resp['status'])
expected_resources = ('nodes', 'chassis',
'ports', 'links', 'media_types')
diff --git a/tempest/api/baremetal/test_chassis.py b/tempest/api/baremetal/test_chassis.py
index 7af1336..4ab86c2 100644
--- a/tempest/api/baremetal/test_chassis.py
+++ b/tempest/api/baremetal/test_chassis.py
@@ -20,57 +20,64 @@
class TestChassis(base.BaseBaremetalTest):
"""Tests for chassis."""
+ @classmethod
+ def setUpClass(cls):
+ super(TestChassis, cls).setUpClass()
+ _, cls.chassis = cls.create_chassis()
+
+ def _assertExpected(self, expected, actual):
+ # Check if not expected keys/values exists in actual response body
+ for key, value in expected.iteritems():
+ if key not in ('created_at', 'updated_at'):
+ self.assertIn(key, actual)
+ self.assertEqual(value, actual[key])
+
@test.attr(type='smoke')
def test_create_chassis(self):
descr = data_utils.rand_name('test-chassis-')
- ch = self.create_chassis(description=descr)['chassis']
-
- self.assertEqual(ch['description'], descr)
+ resp, chassis = self.create_chassis(description=descr)
+ self.assertEqual('201', resp['status'])
+ self.assertEqual(chassis['description'], descr)
@test.attr(type='smoke')
def test_create_chassis_unicode_description(self):
# Use a unicode string for testing:
# 'We ♡ OpenStack in Ukraine'
descr = u'В Україні ♡ OpenStack!'
- ch = self.create_chassis(description=descr)['chassis']
-
- self.assertEqual(ch['description'], descr)
+ resp, chassis = self.create_chassis(description=descr)
+ self.assertEqual('201', resp['status'])
+ self.assertEqual(chassis['description'], descr)
@test.attr(type='smoke')
def test_show_chassis(self):
- descr = data_utils.rand_name('test-chassis-')
- uuid = self.create_chassis(description=descr)['chassis']['uuid']
-
- resp, chassis = self.client.show_chassis(uuid)
-
- self.assertEqual(chassis['uuid'], uuid)
- self.assertEqual(chassis['description'], descr)
+ resp, chassis = self.client.show_chassis(self.chassis['uuid'])
+ self.assertEqual('200', resp['status'])
+ self._assertExpected(self.chassis, chassis)
@test.attr(type="smoke")
def test_list_chassis(self):
- created_ids = [self.create_chassis()['chassis']['uuid']
- for i in range(0, 5)]
-
resp, body = self.client.list_chassis()
- loaded_ids = [ch['uuid'] for ch in body['chassis']]
-
- for i in created_ids:
- self.assertIn(i, loaded_ids)
+ self.assertEqual('200', resp['status'])
+ self.assertIn(self.chassis['uuid'],
+ [i['uuid'] for i in body['chassis']])
@test.attr(type='smoke')
def test_delete_chassis(self):
- uuid = self.create_chassis()['chassis']['uuid']
+ resp, body = self.create_chassis()
+ uuid = body['uuid']
- self.delete_chassis(uuid)
-
+ resp = self.delete_chassis(uuid)
+ self.assertEqual('204', resp['status'])
self.assertRaises(exc.NotFound, self.client.show_chassis, uuid)
@test.attr(type='smoke')
def test_update_chassis(self):
- chassis_id = self.create_chassis()['chassis']['uuid']
+ resp, body = self.create_chassis()
+ uuid = body['uuid']
new_description = data_utils.rand_name('new-description-')
- self.client.update_chassis(chassis_id, description=new_description)
-
- resp, chassis = self.client.show_chassis(chassis_id)
+ resp, body = (self.client.update_chassis(uuid,
+ description=new_description))
+ self.assertEqual('200', resp['status'])
+ resp, chassis = self.client.show_chassis(uuid)
self.assertEqual(chassis['description'], new_description)
diff --git a/tempest/api/baremetal/test_nodes.py b/tempest/api/baremetal/test_nodes.py
index 0f585cb..b6432ad 100644
--- a/tempest/api/baremetal/test_nodes.py
+++ b/tempest/api/baremetal/test_nodes.py
@@ -23,7 +23,15 @@
def setUp(self):
super(TestNodes, self).setUp()
- self.chassis = self.create_chassis()['chassis']
+ _, self.chassis = self.create_chassis()
+ _, self.node = self.create_node(self.chassis['uuid'])
+
+ def _assertExpected(self, expected, actual):
+ # Check if not expected keys/values exists in actual response body
+ for key, value in six.iteritems(expected):
+ if key not in ('created_at', 'updated_at'):
+ self.assertIn(key, actual)
+ self.assertEqual(value, actual[key])
@test.attr(type='smoke')
def test_create_node(self):
@@ -32,45 +40,32 @@
'storage': '10240',
'memory': '1024'}
- node = self.create_node(self.chassis['uuid'], **params)['node']
-
- for key in params:
- self.assertEqual(node['properties'][key], params[key])
+ resp, body = self.create_node(self.chassis['uuid'], **params)
+ self.assertEqual('201', resp['status'])
+ self._assertExpected(params, body['properties'])
@test.attr(type='smoke')
def test_delete_node(self):
- node = self.create_node(self.chassis['uuid'])['node']
- node_id = node['uuid']
+ resp, node = self.create_node(self.chassis['uuid'])
+ self.assertEqual('201', resp['status'])
- resp = self.delete_node(node_id)
+ resp = self.delete_node(node['uuid'])
self.assertEqual(resp['status'], '204')
- self.assertRaises(exc.NotFound, self.client.show_node, node_id)
+ self.assertRaises(exc.NotFound, self.client.show_node, node['uuid'])
@test.attr(type='smoke')
def test_show_node(self):
- params = {'cpu_arch': 'x86_64',
- 'cpu_num': '4',
- 'storage': '100',
- 'memory': '512'}
-
- created_node = self.create_node(self.chassis['uuid'], **params)['node']
- resp, loaded_node = self.client.show_node(created_node['uuid'])
-
- for key, val in created_node.iteritems():
- if key not in ('created_at', 'updated_at'):
- self.assertEqual(loaded_node[key], val)
+ resp, loaded_node = self.client.show_node(self.node['uuid'])
+ self.assertEqual('200', resp['status'])
+ self._assertExpected(self.node, loaded_node)
@test.attr(type='smoke')
def test_list_nodes(self):
- uuids = [self.create_node(self.chassis['uuid'])['node']['uuid']
- for i in range(0, 5)]
-
resp, body = self.client.list_nodes()
- loaded_uuids = [n['uuid'] for n in body['nodes']]
-
- for u in uuids:
- self.assertIn(u, loaded_uuids)
+ self.assertEqual('200', resp['status'])
+ self.assertIn(self.node['uuid'],
+ [i['uuid'] for i in body['nodes']])
@test.attr(type='smoke')
def test_update_node(self):
@@ -79,17 +74,16 @@
'storage': '10',
'memory': '128'}
- node = self.create_node(self.chassis['uuid'], **props)['node']
- node_id = node['uuid']
+ resp, node = self.create_node(self.chassis['uuid'], **props)
+ self.assertEqual('201', resp['status'])
- new_props = {'cpu_arch': 'x86',
- 'cpu_num': '1',
- 'storage': '10000',
- 'memory': '12300'}
+ new_p = {'cpu_arch': 'x86',
+ 'cpu_num': '1',
+ 'storage': '10000',
+ 'memory': '12300'}
- self.client.update_node(node_id, properties=new_props)
- resp, node = self.client.show_node(node_id)
-
- for name, value in six.iteritems(new_props):
- if name not in ('created_at', 'updated_at'):
- self.assertEqual(node['properties'][name], value)
+ resp, body = self.client.update_node(node['uuid'], properties=new_p)
+ self.assertEqual('200', resp['status'])
+ resp, node = self.client.show_node(node['uuid'])
+ self.assertEqual('200', resp['status'])
+ self._assertExpected(new_p, node['properties'])
diff --git a/tempest/api/baremetal/test_nodestates.py b/tempest/api/baremetal/test_nodestates.py
index c658d7f..3044bc6 100644
--- a/tempest/api/baremetal/test_nodestates.py
+++ b/tempest/api/baremetal/test_nodestates.py
@@ -13,6 +13,8 @@
# under the License.
from tempest.api.baremetal import base
+from tempest import exceptions
+from tempest.openstack.common import timeutils
from tempest import test
@@ -20,10 +22,25 @@
"""Tests for baremetal NodeStates."""
@classmethod
- def setUpClass(self):
- super(TestNodeStates, self).setUpClass()
- chassis = self.create_chassis()['chassis']
- self.node = self.create_node(chassis['uuid'])['node']
+ def setUpClass(cls):
+ super(TestNodeStates, cls).setUpClass()
+ resp, cls.chassis = cls.create_chassis()
+ resp, cls.node = cls.create_node(cls.chassis['uuid'])
+
+ def _validate_power_state(self, node_uuid, power_state):
+ # Validate that power state is set within timeout
+ if power_state == 'rebooting':
+ power_state = 'power on'
+ start = timeutils.utcnow()
+ while timeutils.delta_seconds(
+ start, timeutils.utcnow()) < self.power_timeout:
+ resp, node = self.client.show_node(node_uuid)
+ self.assertEqual(200, resp.status)
+ if node['power_state'] == power_state:
+ return
+ message = ('Failed to set power state within '
+ 'the required time: %s sec.' % self.power_timeout)
+ raise exceptions.TimeoutException(message)
@test.attr(type='smoke')
def test_list_nodestates(self):
@@ -31,3 +48,16 @@
self.assertEqual('200', resp['status'])
for key in nodestates:
self.assertEqual(nodestates[key], self.node[key])
+
+ @test.attr(type='smoke')
+ def test_set_node_power_state(self):
+ resp, node = self.create_node(self.chassis['uuid'])
+ self.assertEqual('201', resp['status'])
+ states = ["power on", "rebooting", "power off"]
+ for state in states:
+ # Set power state
+ resp, _ = self.client.set_node_power_state(node['uuid'],
+ state)
+ self.assertEqual('202', resp['status'])
+ # Check power state after state is set
+ self._validate_power_state(node['uuid'], state)
diff --git a/tempest/api/baremetal/test_ports.py b/tempest/api/baremetal/test_ports.py
index aeb77e3..c2af29a 100644
--- a/tempest/api/baremetal/test_ports.py
+++ b/tempest/api/baremetal/test_ports.py
@@ -22,25 +22,30 @@
def setUp(self):
super(TestPorts, self).setUp()
- chassis = self.create_chassis()['chassis']
- self.node = self.create_node(chassis['uuid'])['node']
+ _, self.chassis = self.create_chassis()
+ _, self.node = self.create_node(self.chassis['uuid'])
+ _, self.port = self.create_port(self.node['uuid'],
+ data_utils.rand_mac_address())
+
+ def _assertExpected(self, expected, actual):
+ # Check if not expected keys/values exists in actual response body
+ for key, value in expected.iteritems():
+ if key not in ('created_at', 'updated_at'):
+ self.assertIn(key, actual)
+ self.assertEqual(value, actual[key])
@test.attr(type='smoke')
def test_create_port(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- result = self.create_port(node_id=node_id, address=address)
-
- port = result['port']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual(201, resp.status)
resp, body = self.client.show_port(port['uuid'])
self.assertEqual(200, resp.status)
- self.assertEqual(port['uuid'], body['uuid'])
- self.assertEqual(address, body['address'])
- self.assertEqual({}, body['extra'])
- self.assertEqual(node_id, body['node_uuid'])
+ self._assertExpected(port, body)
@test.attr(type='smoke')
def test_create_port_specifying_uuid(self):
@@ -48,15 +53,13 @@
address = data_utils.rand_mac_address()
uuid = data_utils.rand_uuid()
- self.create_port(node_id=node_id, address=address, uuid=uuid)
+ resp, port = self.create_port(node_id=node_id,
+ address=address, uuid=uuid)
+ self.assertEqual(201, resp.status)
resp, body = self.client.show_port(uuid)
-
self.assertEqual(200, resp.status)
- self.assertEqual(uuid, body['uuid'])
- self.assertEqual(address, body['address'])
- self.assertEqual({}, body['extra'])
- self.assertEqual(node_id, body['node_uuid'])
+ self._assertExpected(port, body)
@test.attr(type='smoke')
def test_create_port_with_extra(self):
@@ -64,76 +67,46 @@
address = data_utils.rand_mac_address()
extra = {'key': 'value'}
- result = self.create_port(node_id=node_id, address=address,
- extra=extra)
- port = result['port']
+ resp, port = self.create_port(node_id=node_id, address=address,
+ extra=extra)
+ self.assertEqual(201, resp.status)
resp, body = self.client.show_port(port['uuid'])
-
self.assertEqual(200, resp.status)
- self.assertEqual(port['uuid'], body['uuid'])
- self.assertEqual(address, body['address'])
- self.assertEqual(extra, body['extra'])
- self.assertEqual(node_id, body['node_uuid'])
+ self._assertExpected(port, body)
@test.attr(type='smoke')
def test_delete_port(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- port_id = self.create_port(node_id=node_id, address=address)['port'][
- 'uuid']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual(201, resp.status)
- resp = self.delete_port(port_id)
+ resp = self.delete_port(port['uuid'])
self.assertEqual(204, resp.status)
- self.assertRaises(exc.NotFound, self.client.show_port, port_id)
+ self.assertRaises(exc.NotFound, self.client.show_port, port['uuid'])
@test.attr(type='smoke')
def test_show_port(self):
- node_id = self.node['uuid']
- address = data_utils.rand_mac_address()
- extra = {'key': 'value'}
-
- port_id = self.create_port(node_id=node_id, address=address,
- extra=extra)['port']['uuid']
-
- resp, port = self.client.show_port(port_id)
-
+ resp, port = self.client.show_port(self.port['uuid'])
self.assertEqual(200, resp.status)
- self.assertEqual(port_id, port['uuid'])
- self.assertEqual(address, port['address'])
- self.assertEqual(extra, port['extra'])
+ self._assertExpected(self.port, port)
@test.attr(type='smoke')
def test_show_port_with_links(self):
- node_id = self.node['uuid']
- address = data_utils.rand_mac_address()
-
- port_id = self.create_port(node_id=node_id, address=address)['port'][
- 'uuid']
-
- resp, body = self.client.show_port(port_id)
-
+ resp, port = self.client.show_port(self.port['uuid'])
self.assertEqual(200, resp.status)
- self.assertIn('links', body.keys())
- self.assertEqual(2, len(body['links']))
- self.assertIn(port_id, body['links'][0]['href'])
+ self.assertIn('links', port.keys())
+ self.assertEqual(2, len(port['links']))
+ self.assertIn(port['uuid'], port['links'][0]['href'])
@test.attr(type='smoke')
def test_list_ports(self):
- node_id = self.node['uuid']
-
- uuids = [self.create_port(node_id=node_id,
- address=data_utils.rand_mac_address())
- ['port']['uuid'] for i in xrange(5)]
-
resp, body = self.client.list_ports()
self.assertEqual(200, resp.status)
- loaded_uuids = [p['uuid'] for p in body['ports']]
-
- for uuid in uuids:
- self.assertIn(uuid, loaded_uuids)
-
+ self.assertIn(self.port['uuid'],
+ [i['uuid'] for i in body['ports']])
# Verify self links.
for port in body['ports']:
self.validate_self_link('ports', port['uuid'],
@@ -141,15 +114,8 @@
@test.attr(type='smoke')
def test_list_with_limit(self):
- node_id = self.node['uuid']
-
- for i in xrange(5):
- self.create_port(node_id=node_id,
- address=data_utils.rand_mac_address())
-
resp, body = self.client.list_ports(limit=3)
self.assertEqual(200, resp.status)
- self.assertEqual(3, len(body['ports']))
next_marker = body['ports'][-1]['uuid']
self.assertIn(next_marker, body['next'])
@@ -160,7 +126,7 @@
uuids = [
self.create_port(node_id=node_id,
address=data_utils.rand_mac_address())
- ['port']['uuid'] for i in range(0, 5)]
+ [1]['uuid'] for i in range(0, 5)]
resp, body = self.client.list_ports_detail()
self.assertEqual(200, resp.status)
@@ -185,8 +151,9 @@
address = data_utils.rand_mac_address()
extra = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
- port_id = self.create_port(node_id=node_id, address=address,
- extra=extra)['port']['uuid']
+ resp, port = self.create_port(node_id=node_id, address=address,
+ extra=extra)
+ self.assertEqual(201, resp.status)
new_address = data_utils.rand_mac_address()
new_extra = {'key1': 'new-value1', 'key2': 'new-value2',
@@ -205,9 +172,10 @@
'op': 'replace',
'value': new_extra['key3']}]
- self.client.update_port(port_id, patch)
+ resp, _ = self.client.update_port(port['uuid'], patch)
+ self.assertEqual(200, resp.status)
- resp, body = self.client.show_port(port_id)
+ resp, body = self.client.show_port(port['uuid'])
self.assertEqual(200, resp.status)
self.assertEqual(new_address, body['address'])
self.assertEqual(new_extra, body['extra'])
@@ -218,23 +186,25 @@
address = data_utils.rand_mac_address()
extra = {'key1': 'value1', 'key2': 'value2', 'key3': 'value3'}
- port_id = self.create_port(node_id=node_id, address=address,
- extra=extra)['port']['uuid']
+ resp, port = self.create_port(node_id=node_id, address=address,
+ extra=extra)
+ self.assertEqual(201, resp.status)
# Removing one item from the collection
- resp, _ = self.client.update_port(port_id, [{'path': '/extra/key2',
- 'op': 'remove'}])
+ resp, _ = self.client.update_port(port['uuid'],
+ [{'path': '/extra/key2',
+ 'op': 'remove'}])
self.assertEqual(200, resp.status)
extra.pop('key2')
- resp, body = self.client.show_port(port_id)
+ resp, body = self.client.show_port(port['uuid'])
self.assertEqual(200, resp.status)
self.assertEqual(extra, body['extra'])
# Removing the collection
- resp, _ = self.client.update_port(port_id, [{'path': '/extra',
- 'op': 'remove'}])
+ resp, _ = self.client.update_port(port['uuid'], [{'path': '/extra',
+ 'op': 'remove'}])
self.assertEqual(200, resp.status)
- resp, body = self.client.show_port(port_id)
+ resp, body = self.client.show_port(port['uuid'])
self.assertEqual(200, resp.status)
self.assertEqual({}, body['extra'])
@@ -247,8 +217,8 @@
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- port_id = self.create_port(node_id=node_id, address=address)['port'][
- 'uuid']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual(201, resp.status)
extra = {'key1': 'value1', 'key2': 'value2'}
@@ -259,9 +229,10 @@
'op': 'add',
'value': extra['key2']}]
- self.client.update_port(port_id, patch)
+ resp, _ = self.client.update_port(port['uuid'], patch)
+ self.assertEqual(200, resp.status)
- resp, body = self.client.show_port(port_id)
+ resp, body = self.client.show_port(port['uuid'])
self.assertEqual(200, resp.status)
self.assertEqual(extra, body['extra'])
@@ -271,8 +242,9 @@
address = data_utils.rand_mac_address()
extra = {'key1': 'value1', 'key2': 'value2'}
- port_id = self.create_port(node_id=node_id, address=address,
- extra=extra)['port']['uuid']
+ resp, port = self.create_port(node_id=node_id, address=address,
+ extra=extra)
+ self.assertEqual(201, resp.status)
new_address = data_utils.rand_mac_address()
new_extra = {'key1': 'new-value1', 'key3': 'new-value3'}
@@ -289,9 +261,10 @@
'op': 'add',
'value': new_extra['key3']}]
- self.client.update_port(port_id, patch)
+ resp, _ = self.client.update_port(port['uuid'], patch)
+ self.assertEqual(200, resp.status)
- resp, body = self.client.show_port(port_id)
+ resp, body = self.client.show_port(port['uuid'])
self.assertEqual(200, resp.status)
self.assertEqual(new_address, body['address'])
self.assertEqual(new_extra, body['extra'])
diff --git a/tempest/api/baremetal/test_ports_negative.py b/tempest/api/baremetal/test_ports_negative.py
index 4cbe00e..3e77a5f 100644
--- a/tempest/api/baremetal/test_ports_negative.py
+++ b/tempest/api/baremetal/test_ports_negative.py
@@ -22,8 +22,11 @@
def setUp(self):
super(TestPortsNegative, self).setUp()
- chassis = self.create_chassis()['chassis']
- self.node = self.create_node(chassis['uuid'])['node']
+ resp, self.chassis = self.create_chassis()
+ self.assertEqual('201', resp['status'])
+
+ resp, self.node = self.create_node(self.chassis['uuid'])
+ self.assertEqual('201', resp['status'])
@test.attr(type=['negative', 'smoke'])
def test_create_port_malformed_mac(self):
@@ -134,9 +137,13 @@
address = data_utils.rand_mac_address()
extra = {'key': 'value'}
- port_id = self.create_port(node_id=node_id, address=address,
- extra=extra)['port']['uuid']
- self.client.delete_port(port_id)
+ resp, port = self.create_port(node_id=node_id, address=address,
+ extra=extra)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
+
+ resp, body = self.client.delete_port(port_id)
+ self.assertEqual('204', resp['status'])
patch = [{'path': '/extra/key',
'op': 'replace',
@@ -162,8 +169,9 @@
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- port_id = self.create_port(node_id=node_id, address=address)['port'][
- 'uuid']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
self.assertRaises(exc.BadRequest, self.client.update_port, port_id,
[{'path': '/extra/key', ' op': 'add',
@@ -174,8 +182,9 @@
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- port_id = self.create_port(node_id=node_id, address=address)['port'][
- 'uuid']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
self.assertRaises(exc.BadRequest, self.client.update_port, port_id,
[{'path': '/extra',
@@ -187,8 +196,10 @@
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- port_id = self.create_port(node_id=node_id, address=address)['port'][
- 'uuid']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
+
self.assertRaises(exc.BadRequest, self.client.update_port, port_id,
[{'path': '/nonexistent', ' op': 'add',
'value': 'value'}])
@@ -198,8 +209,9 @@
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- port_id = self.create_port(node_id=node_id,
- address=address)['port']['uuid']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
patch = [{'path': '/node_uuid',
'op': 'replace',
@@ -213,9 +225,13 @@
address1 = data_utils.rand_mac_address()
address2 = data_utils.rand_mac_address()
- self.create_port(node_id=node_id, address=address1)
- port_id = self.create_port(node_id=node_id,
- address=address2)['port']['uuid']
+ resp, port1 = self.create_port(node_id=node_id, address=address1)
+ self.assertEqual('201', resp['status'])
+
+ resp, port2 = self.create_port(node_id=node_id, address=address2)
+ self.assertEqual('201', resp['status'])
+ port_id = port2['uuid']
+
patch = [{'path': '/address',
'op': 'replace',
'value': address1}]
@@ -227,8 +243,9 @@
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- port_id = self.create_port(node_id=node_id,
- address=address)['port']['uuid']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
patch = [{'path': '/node_uuid',
'op': 'replace',
@@ -241,8 +258,10 @@
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- port_id = self.create_port(node_id=node_id,
- address=address)['port']['uuid']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
+
patch = [{'path': '/address',
'op': 'replace',
'value': 'malformed:mac'}]
@@ -256,13 +275,14 @@
address = data_utils.rand_mac_address()
extra = {'key': 'value'}
- port_id = self.create_port(node_id=node_id,
- address=address,
- extra=extra)['port']['uuid']
+ resp, port = self.create_port(node_id=node_id, address=address,
+ extra=extra)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
+
patch = [{'path': '/extra/key',
'op': 'replace',
'value': 0.123}]
-
self.assertRaises(exc.BadRequest,
self.client.update_port, port_id, patch)
@@ -270,11 +290,11 @@
def test_update_port_replace_whole_extra_with_malformed(self):
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- extra = {'key': 'value'}
- port_id = self.create_port(node_id=node_id,
- address=address,
- extra=extra)['port']['uuid']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
+
patch = [{'path': '/extra',
'op': 'replace',
'value': [1, 2, 3, 4, 'a']}]
@@ -287,8 +307,9 @@
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- port_id = self.create_port(node_id=node_id,
- address=address)['port']['uuid']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
patch = [{'path': '/nonexistent', ' op': 'replace', 'value': 'value'}]
@@ -300,8 +321,10 @@
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- port_id = self.create_port(node_id=node_id, address=address)['port'][
- 'uuid']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
+
self.assertRaises(exc.BadRequest, self.client.update_port, port_id,
[{'path': '/address', 'op': 'remove'}])
@@ -310,8 +333,10 @@
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- port_id = self.create_port(node_id=node_id, address=address)['port'][
- 'uuid']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
+
self.assertRaises(exc.BadRequest, self.client.update_port, port_id,
[{'path': '/uuid', 'op': 'remove'}])
@@ -320,8 +345,10 @@
node_id = self.node['uuid']
address = data_utils.rand_mac_address()
- port_id = self.create_port(node_id=node_id, address=address)['port'][
- 'uuid']
+ resp, port = self.create_port(node_id=node_id, address=address)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
+
self.assertRaises(exc.BadRequest, self.client.update_port, port_id,
[{'path': '/nonexistent', 'op': 'remove'}])
@@ -339,8 +366,10 @@
address = data_utils.rand_mac_address()
extra = {'key1': 'value1', 'key2': 'value2'}
- port_id = self.create_port(node_id=node_id, address=address,
- extra=extra)['port']['uuid']
+ resp, port = self.create_port(node_id=node_id, address=address,
+ extra=extra)
+ self.assertEqual('201', resp['status'])
+ port_id = port['uuid']
new_address = data_utils.rand_mac_address()
new_extra = {'key1': 'new-value1', 'key3': 'new-value3'}
diff --git a/tempest/api/compute/admin/test_flavors.py b/tempest/api/compute/admin/test_flavors.py
index 111ac9c..18866e5 100644
--- a/tempest/api/compute/admin/test_flavors.py
+++ b/tempest/api/compute/admin/test_flavors.py
@@ -30,8 +30,8 @@
@classmethod
def setUpClass(cls):
super(FlavorsAdminTestJSON, cls).setUpClass()
- if not test.is_extension_enabled('FlavorExtraData', 'compute'):
- msg = "FlavorExtraData extension not enabled."
+ if not test.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
+ msg = "OS-FLV-EXT-DATA extension not enabled."
raise cls.skipException(msg)
cls.client = cls.os_adm.flavors_client
@@ -231,7 +231,7 @@
flavor_name = data_utils.rand_name(self.flavor_name_prefix)
new_flavor_id = data_utils.rand_int_id(start=1000)
- # Create the flavor
+ # Create the flavor
resp, flavor = self.client.create_flavor(flavor_name,
self.ram, self.vcpus,
self.disk,
diff --git a/tempest/api/compute/admin/test_flavors_access.py b/tempest/api/compute/admin/test_flavors_access.py
index 3ba7314..f2554ea 100644
--- a/tempest/api/compute/admin/test_flavors_access.py
+++ b/tempest/api/compute/admin/test_flavors_access.py
@@ -28,8 +28,8 @@
@classmethod
def setUpClass(cls):
super(FlavorsAccessTestJSON, cls).setUpClass()
- if not test.is_extension_enabled('FlavorExtraData', 'compute'):
- msg = "FlavorExtraData extension not enabled."
+ if not test.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
+ msg = "OS-FLV-EXT-DATA extension not enabled."
raise cls.skipException(msg)
# Compute admin flavor client
diff --git a/tempest/api/compute/admin/test_flavors_access_negative.py b/tempest/api/compute/admin/test_flavors_access_negative.py
index 73834e9..b636ccd 100644
--- a/tempest/api/compute/admin/test_flavors_access_negative.py
+++ b/tempest/api/compute/admin/test_flavors_access_negative.py
@@ -31,8 +31,8 @@
@classmethod
def setUpClass(cls):
super(FlavorsAccessNegativeTestJSON, cls).setUpClass()
- if not test.is_extension_enabled('FlavorExtraData', 'compute'):
- msg = "FlavorExtraData extension not enabled."
+ if not test.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
+ msg = "OS-FLV-EXT-DATA extension not enabled."
raise cls.skipException(msg)
cls.client = cls.os_adm.flavors_client
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs.py b/tempest/api/compute/admin/test_flavors_extra_specs.py
index 91145ec..56daf96 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs.py
@@ -29,8 +29,8 @@
@classmethod
def setUpClass(cls):
super(FlavorsExtraSpecsTestJSON, cls).setUpClass()
- if not test.is_extension_enabled('FlavorExtraData', 'compute'):
- msg = "FlavorExtraData extension not enabled."
+ if not test.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
+ msg = "OS-FLV-EXT-DATA extension not enabled."
raise cls.skipException(msg)
cls.client = cls.os_adm.flavors_client
diff --git a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
index a139c2f..1e5695f 100644
--- a/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
+++ b/tempest/api/compute/admin/test_flavors_extra_specs_negative.py
@@ -30,8 +30,8 @@
@classmethod
def setUpClass(cls):
super(FlavorsExtraSpecsNegativeTestJSON, cls).setUpClass()
- if not test.is_extension_enabled('FlavorExtraData', 'compute'):
- msg = "FlavorExtraData extension not enabled."
+ if not test.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
+ msg = "OS-FLV-EXT-DATA extension not enabled."
raise cls.skipException(msg)
cls.client = cls.os_adm.flavors_client
diff --git a/tempest/api/compute/admin/test_flavors_negative.py b/tempest/api/compute/admin/test_flavors_negative.py
index b37d32c..9e4412f 100644
--- a/tempest/api/compute/admin/test_flavors_negative.py
+++ b/tempest/api/compute/admin/test_flavors_negative.py
@@ -32,8 +32,8 @@
@classmethod
def setUpClass(cls):
super(FlavorsAdminNegativeTestJSON, cls).setUpClass()
- if not test.is_extension_enabled('FlavorExtraData', 'compute'):
- msg = "FlavorExtraData extension not enabled."
+ if not test.is_extension_enabled('OS-FLV-EXT-DATA', 'compute'):
+ msg = "OS-FLV-EXT-DATA extension not enabled."
raise cls.skipException(msg)
cls.client = cls.os_adm.flavors_client
diff --git a/tempest/api/compute/admin/test_floating_ips_bulk.py b/tempest/api/compute/admin/test_floating_ips_bulk.py
new file mode 100644
index 0000000..8f875d2
--- /dev/null
+++ b/tempest/api/compute/admin/test_floating_ips_bulk.py
@@ -0,0 +1,82 @@
+# Copyright 2014 NEC Technologies India Ltd.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import netaddr
+
+from tempest.api.compute import base
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class FloatingIPsBulkAdminTestJSON(base.BaseV2ComputeAdminTest):
+ """
+ Tests Floating IPs Bulk APIs Create, List and Delete that
+ require admin privileges.
+ API documentation - http://docs.openstack.org/api/openstack-compute/2/
+ content/ext-os-floating-ips-bulk.html
+ """
+
+ @classmethod
+ @test.safe_setup
+ def setUpClass(cls):
+ super(FloatingIPsBulkAdminTestJSON, cls).setUpClass()
+ cls.client = cls.os_adm.floating_ips_client
+ cls.ip_range = CONF.compute.floating_ip_range
+ cls.verify_unallocated_floating_ip_range(cls.ip_range)
+
+ @classmethod
+ def verify_unallocated_floating_ip_range(cls, ip_range):
+ # Verify whether configure floating IP range is not already allocated.
+ _, body = cls.client.list_floating_ips_bulk()
+ allocated_ips_list = map(lambda x: x['address'], body)
+ for ip_addr in netaddr.IPNetwork(ip_range).iter_hosts():
+ if str(ip_addr) in allocated_ips_list:
+ msg = ("Configured unallocated floating IP range is already "
+ "allocated. Configure the correct unallocated range "
+ "as 'floating_ip_range'")
+ raise cls.skipException(msg)
+ return
+
+ def _delete_floating_ips_bulk(self, ip_range):
+ try:
+ self.client.delete_floating_ips_bulk(ip_range)
+ except Exception:
+ pass
+
+ @test.attr(type='gate')
+ def test_create_list_delete_floating_ips_bulk(self):
+ # Create, List and delete the Floating IPs Bulk
+ pool = 'test_pool'
+ # NOTE(GMann): Reserving the IP range but those are not attached
+ # anywhere. Using the below mentioned interface which is not ever
+ # expected to be used. Clean Up has been done for created IP range
+ interface = 'eth0'
+ resp, body = self.client.create_floating_ips_bulk(self.ip_range,
+ pool,
+ interface)
+
+ self.assertEqual(200, resp.status)
+ self.addCleanup(self._delete_floating_ips_bulk, self.ip_range)
+ self.assertEqual(self.ip_range, body['ip_range'])
+ resp, ips_list = self.client.list_floating_ips_bulk()
+ self.assertEqual(200, resp.status)
+ self.assertNotEqual(0, len(body))
+ for ip in netaddr.IPNetwork(self.ip_range).iter_hosts():
+ self.assertIn(str(ip), map(lambda x: x['address'], ips_list))
+ resp, body = self.client.delete_floating_ips_bulk(self.ip_range)
+ self.assertEqual(200, resp.status)
+ self.assertEqual(self.ip_range, body)
diff --git a/tempest/api/compute/images/test_images_negative.py b/tempest/api/compute/images/test_images_negative.py
index ae00ae2..6163f4d 100644
--- a/tempest/api/compute/images/test_images_negative.py
+++ b/tempest/api/compute/images/test_images_negative.py
@@ -40,7 +40,7 @@
# Delete server before trying to create server
self.servers_client.delete_server(server['id'])
self.servers_client.wait_for_server_termination(server['id'])
- # Create a new image after server is deleted
+ # Create a new image after server is deleted
name = data_utils.rand_name('image')
meta = {'image_type': 'test'}
self.assertRaises(exceptions.NotFound,
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 5ac667e..6343ead 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -45,7 +45,10 @@
def setUpClass(cls):
super(ServersNegativeTestJSON, cls).setUpClass()
cls.client = cls.servers_client
- cls.alt_os = clients.AltManager()
+ if CONF.compute.allow_tenant_isolation:
+ cls.alt_os = clients.Manager(cls.isolated_creds.get_alt_creds())
+ else:
+ cls.alt_os = clients.AltManager()
cls.alt_client = cls.alt_os.servers_client
resp, server = cls.create_test_server(wait_until='ACTIVE')
cls.server_id = server['id']
diff --git a/tempest/api/compute/v3/admin/test_flavors.py b/tempest/api/compute/v3/admin/test_flavors.py
index 2a4fc02..8a4e3cf 100644
--- a/tempest/api/compute/v3/admin/test_flavors.py
+++ b/tempest/api/compute/v3/admin/test_flavors.py
@@ -229,7 +229,7 @@
flavor_name = data_utils.rand_name(self.flavor_name_prefix)
new_flavor_id = data_utils.rand_int_id(start=1000)
- # Create the flavor
+ # Create the flavor
resp, flavor = self.client.create_flavor(flavor_name,
self.ram, self.vcpus,
self.disk,
diff --git a/tempest/api/compute/v3/servers/test_servers_negative.py b/tempest/api/compute/v3/servers/test_servers_negative.py
index 827c4c4..90deaa9 100644
--- a/tempest/api/compute/v3/servers/test_servers_negative.py
+++ b/tempest/api/compute/v3/servers/test_servers_negative.py
@@ -45,7 +45,10 @@
def setUpClass(cls):
super(ServersNegativeV3Test, cls).setUpClass()
cls.client = cls.servers_client
- cls.alt_os = clients.AltManager()
+ if CONF.compute.allow_tenant_isolation:
+ cls.alt_os = clients.Manager(cls.isolated_creds.get_alt_creds())
+ else:
+ cls.alt_os = clients.AltManager()
cls.alt_client = cls.alt_os.servers_v3_client
resp, server = cls.create_test_server(wait_until='ACTIVE')
cls.server_id = server['id']
diff --git a/tempest/api/compute/volumes/test_attach_volume.py b/tempest/api/compute/volumes/test_attach_volume.py
index 4585912..5a64544 100644
--- a/tempest/api/compute/volumes/test_attach_volume.py
+++ b/tempest/api/compute/volumes/test_attach_volume.py
@@ -53,28 +53,28 @@
def _create_and_attach(self):
# Start a server and wait for it to become ready
admin_pass = self.image_ssh_password
- resp, server = self.create_test_server(wait_until='ACTIVE',
- adminPass=admin_pass)
- self.server = server
+ _, self.server = self.create_test_server(wait_until='ACTIVE',
+ adminPass=admin_pass)
# Record addresses so that we can ssh later
- resp, server['addresses'] = \
- self.servers_client.list_addresses(server['id'])
+ _, self.server['addresses'] = \
+ self.servers_client.list_addresses(self.server['id'])
# Create a volume and wait for it to become ready
- resp, volume = self.volumes_client.create_volume(1,
- display_name='test')
- self.volume = volume
+ _, self.volume = self.volumes_client.create_volume(
+ 1, display_name='test')
self.addCleanup(self._delete_volume)
- self.volumes_client.wait_for_volume_status(volume['id'], 'available')
+ self.volumes_client.wait_for_volume_status(self.volume['id'],
+ 'available')
# Attach the volume to the server
- self.servers_client.attach_volume(server['id'], volume['id'],
+ self.servers_client.attach_volume(self.server['id'],
+ self.volume['id'],
device='/dev/%s' % self.device)
- self.volumes_client.wait_for_volume_status(volume['id'], 'in-use')
+ self.volumes_client.wait_for_volume_status(self.volume['id'], 'in-use')
self.attached = True
- self.addCleanup(self._detach, server['id'], volume['id'])
+ self.addCleanup(self._detach, self.server['id'], self.volume['id'])
@testtools.skipUnless(CONF.compute.run_ssh, 'SSH required for this test')
@test.attr(type='gate')
@@ -82,31 +82,33 @@
# Stop and Start a server with an attached volume, ensuring that
# the volume remains attached.
self._create_and_attach()
- server = self.server
- volume = self.volume
- self.servers_client.stop(server['id'])
- self.servers_client.wait_for_server_status(server['id'], 'SHUTOFF')
+ self.servers_client.stop(self.server['id'])
+ self.servers_client.wait_for_server_status(self.server['id'],
+ 'SHUTOFF')
- self.servers_client.start(server['id'])
- self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+ self.servers_client.start(self.server['id'])
+ self.servers_client.wait_for_server_status(self.server['id'], 'ACTIVE')
- linux_client = remote_client.RemoteClient(server, self.image_ssh_user,
- server['adminPass'])
+ linux_client = remote_client.RemoteClient(self.server,
+ self.image_ssh_user,
+ self.server['adminPass'])
partitions = linux_client.get_partitions()
self.assertIn(self.device, partitions)
- self._detach(server['id'], volume['id'])
+ self._detach(self.server['id'], self.volume['id'])
self.attached = False
- self.servers_client.stop(server['id'])
- self.servers_client.wait_for_server_status(server['id'], 'SHUTOFF')
+ self.servers_client.stop(self.server['id'])
+ self.servers_client.wait_for_server_status(self.server['id'],
+ 'SHUTOFF')
- self.servers_client.start(server['id'])
- self.servers_client.wait_for_server_status(server['id'], 'ACTIVE')
+ self.servers_client.start(self.server['id'])
+ self.servers_client.wait_for_server_status(self.server['id'], 'ACTIVE')
- linux_client = remote_client.RemoteClient(server, self.image_ssh_user,
- server['adminPass'])
+ linux_client = remote_client.RemoteClient(self.server,
+ self.image_ssh_user,
+ self.server['adminPass'])
partitions = linux_client.get_partitions()
self.assertNotIn(self.device, partitions)
diff --git a/tempest/api/data_processing/base.py b/tempest/api/data_processing/base.py
index cc76880..0d6773c 100644
--- a/tempest/api/data_processing/base.py
+++ b/tempest/api/data_processing/base.py
@@ -39,6 +39,7 @@
cls._cluster_templates = []
cls._data_sources = []
cls._job_binary_internals = []
+ cls._job_binaries = []
@classmethod
def tearDownClass(cls):
@@ -50,6 +51,8 @@
cls.client.delete_data_source)
cls.cleanup_resources(getattr(cls, '_job_binary_internals', []),
cls.client.delete_job_binary_internal)
+ cls.cleanup_resources(getattr(cls, '_job_binaries', []),
+ cls.client.delete_job_binary)
cls.clear_isolated_creds()
super(BaseDataProcessingTest, cls).tearDownClass()
@@ -128,3 +131,16 @@
cls._job_binary_internals.append(body['id'])
return resp, body
+
+ def create_job_binary(cls, name, url, extra=None, **kwargs):
+ """Creates watched job binary with specified params.
+
+ It supports passing additional params using kwargs and returns created
+ object. All resources created in this method will be automatically
+ removed in tearDownClass method.
+ """
+ resp, body = cls.client.create_job_binary(name, url, extra, **kwargs)
+ # store id of created job binary
+ cls._job_binaries.append(body['id'])
+
+ return resp, body
diff --git a/tempest/api/data_processing/test_cluster_templates.py b/tempest/api/data_processing/test_cluster_templates.py
index c08d6ba..e5c6303 100644
--- a/tempest/api/data_processing/test_cluster_templates.py
+++ b/tempest/api/data_processing/test_cluster_templates.py
@@ -143,4 +143,4 @@
# delete the cluster template by id
resp = self.client.delete_cluster_template(template_id)[0]
self.assertEqual(204, resp.status)
- #TODO(ylobankov): check that cluster template is really deleted
+ # TODO(ylobankov): check that cluster template is really deleted
diff --git a/tempest/api/database/base.py b/tempest/api/database/base.py
index cf70d11..b68c84a 100644
--- a/tempest/api/database/base.py
+++ b/tempest/api/database/base.py
@@ -41,4 +41,5 @@
os = cls.get_client_manager()
cls.os = os
cls.database_flavors_client = cls.os.database_flavors_client
+ cls.os_flavors_client = cls.os.flavors_client
cls.database_versions_client = cls.os.database_versions_client
diff --git a/tempest/api/database/flavors/test_flavors.py b/tempest/api/database/flavors/test_flavors.py
index a591e8e..64d71b9 100644
--- a/tempest/api/database/flavors/test_flavors.py
+++ b/tempest/api/database/flavors/test_flavors.py
@@ -28,6 +28,7 @@
def test_get_db_flavor(self):
# The expected flavor details should be returned
resp, flavor = self.client.get_db_flavor_details(self.db_flavor_ref)
+ self.assertEqual(200, resp.status)
self.assertEqual(self.db_flavor_ref, str(flavor['id']))
self.assertIn('ram', flavor)
self.assertIn('links', flavor)
@@ -36,6 +37,36 @@
@test.attr(type='smoke')
def test_list_db_flavors(self):
resp, flavor = self.client.get_db_flavor_details(self.db_flavor_ref)
+ self.assertEqual(200, resp.status)
# List of all flavors should contain the expected flavor
resp, flavors = self.client.list_db_flavors()
+ self.assertEqual(200, resp.status)
self.assertIn(flavor, flavors)
+
+ def _check_values(self, names, db_flavor, os_flavor, in_db=True):
+ for name in names:
+ self.assertIn(name, os_flavor)
+ if in_db:
+ self.assertIn(name, db_flavor)
+ self.assertEqual(str(db_flavor[name]), str(os_flavor[name]),
+ "DB flavor differs from OS on '%s' value"
+ % name)
+ else:
+ self.assertNotIn(name, db_flavor)
+
+ @test.attr(type='smoke')
+ def test_compare_db_flavors_with_os(self):
+ resp, db_flavors = self.client.list_db_flavors()
+ self.assertEqual(200, resp.status)
+ resp, os_flavors = self.os_flavors_client.list_flavors_with_detail()
+ self.assertEqual(200, resp.status)
+ self.assertEqual(len(os_flavors), len(db_flavors),
+ "OS flavors %s do not match DB flavors %s" %
+ (os_flavors, db_flavors))
+ for os_flavor in os_flavors:
+ resp, db_flavor =\
+ self.client.get_db_flavor_details(os_flavor['id'])
+ self.assertEqual(200, resp.status)
+ self._check_values(['id', 'name', 'ram'], db_flavor, os_flavor)
+ self._check_values(['disk', 'vcpus', 'swap'], db_flavor, os_flavor,
+ in_db=False)
diff --git a/tempest/api/identity/admin/test_services.py b/tempest/api/identity/admin/test_services.py
index 0472e07..5926488 100644
--- a/tempest/api/identity/admin/test_services.py
+++ b/tempest/api/identity/admin/test_services.py
@@ -101,7 +101,7 @@
# List and Verify Services
resp, body = self.client.list_services()
self.assertEqual(200, resp.status)
- found = [service for service in body if service['id'] in service_ids]
+ found = [serv for serv in body if serv['id'] in service_ids]
self.assertEqual(len(found), len(services), 'Services not found')
diff --git a/tempest/api/identity/admin/test_tenants.py b/tempest/api/identity/admin/test_tenants.py
index b989664..93734d2 100644
--- a/tempest/api/identity/admin/test_tenants.py
+++ b/tempest/api/identity/admin/test_tenants.py
@@ -36,7 +36,7 @@
tenant_ids = map(lambda x: x['id'], tenants)
resp, body = self.client.list_tenants()
self.assertEqual(200, resp.status)
- found = [tenant for tenant in body if tenant['id'] in tenant_ids]
+ found = [t for t in body if t['id'] in tenant_ids]
self.assertEqual(len(found), len(tenants), 'Tenants not created')
for tenant in tenants:
diff --git a/tempest/api/identity/admin/v3/test_projects.py b/tempest/api/identity/admin/v3/test_projects.py
index 31a0ddd..79717b1 100644
--- a/tempest/api/identity/admin/v3/test_projects.py
+++ b/tempest/api/identity/admin/v3/test_projects.py
@@ -171,13 +171,13 @@
@test.attr(type='gate')
def test_associate_user_to_project(self):
- #Associate a user to a project
- #Create a Project
+ # Associate a user to a project
+ # Create a Project
p_name = data_utils.rand_name('project-')
resp, project = self.client.create_project(p_name)
self.data.projects.append(project)
- #Create a User
+ # Create a User
u_name = data_utils.rand_name('user-')
u_desc = u_name + 'description'
u_email = u_name + '@testmail.tm'
@@ -191,7 +191,7 @@
# Get User To validate the user details
resp, new_user_get = self.client.get_user(user['id'])
- #Assert response body of GET
+ # Assert response body of GET
self.assertEqual(u_name, new_user_get['name'])
self.assertEqual(u_desc, new_user_get['description'])
self.assertEqual(project['id'],
diff --git a/tempest/api/network/base.py b/tempest/api/network/base.py
index dcd9bff..cc768fd 100644
--- a/tempest/api/network/base.py
+++ b/tempest/api/network/base.py
@@ -81,9 +81,13 @@
cls.metering_label_rules = []
cls.fw_rules = []
cls.fw_policies = []
+ cls.ipsecpolicies = []
@classmethod
def tearDownClass(cls):
+ # Clean up ipsec policies
+ for ipsecpolicy in cls.ipsecpolicies:
+ cls.client.delete_ipsecpolicy(ipsecpolicy['id'])
# Clean up firewall policies
for fw_policy in cls.fw_policies:
cls.client.delete_firewall_policy(fw_policy['id'])
@@ -145,15 +149,16 @@
return network
@classmethod
- def create_subnet(cls, network, gateway=None):
+ def create_subnet(cls, network, gateway=None, cidr=None, mask_bits=None):
"""Wrapper utility that returns a test subnet."""
# The cidr and mask_bits depend on the ip version.
if cls._ip_version == 4:
- cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr)
- mask_bits = CONF.network.tenant_network_mask_bits
+ cidr = cidr or netaddr.IPNetwork(CONF.network.tenant_network_cidr)
+ mask_bits = mask_bits or CONF.network.tenant_network_mask_bits
elif cls._ip_version == 6:
- cidr = netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr)
- mask_bits = CONF.network.tenant_network_v6_mask_bits
+ cidr = (
+ cidr or netaddr.IPNetwork(CONF.network.tenant_network_v6_cidr))
+ mask_bits = mask_bits or CONF.network.tenant_network_v6_mask_bits
# Find a cidr that is not in use yet and create a subnet with it
for subnet_cidr in cidr.subnet(mask_bits):
if not gateway:
@@ -342,6 +347,14 @@
router['id'], i['fixed_ips'][0]['subnet_id'])
cls.client.delete_router(router['id'])
+ @classmethod
+ def create_ipsecpolicy(cls, name):
+ """Wrapper utility that returns a test ipsec policy."""
+ _, body = cls.client.create_ipsecpolicy(name=name)
+ ipsecpolicy = body['ipsecpolicy']
+ cls.ipsecpolicies.append(ipsecpolicy)
+ return ipsecpolicy
+
class BaseAdminNetworkTest(BaseNetworkTest):
diff --git a/tempest/api/network/base_routers.py b/tempest/api/network/base_routers.py
index b278002..1303bcf 100644
--- a/tempest/api/network/base_routers.py
+++ b/tempest/api/network/base_routers.py
@@ -37,6 +37,15 @@
routers_list.append(router['id'])
self.assertNotIn(router_id, routers_list)
+ def _add_router_interface_with_subnet_id(self, router_id, subnet_id):
+ resp, interface = self.client.add_router_interface_with_subnet_id(
+ router_id, subnet_id)
+ self.assertEqual('200', resp['status'])
+ self.addCleanup(self._remove_router_interface_with_subnet_id,
+ router_id, subnet_id)
+ self.assertEqual(subnet_id, interface['subnet_id'])
+ return interface
+
def _remove_router_interface_with_subnet_id(self, router_id, subnet_id):
resp, body = self.client.remove_router_interface_with_subnet_id(
router_id, subnet_id)
diff --git a/tempest/api/network/test_load_balancer.py b/tempest/api/network/test_load_balancer.py
index 673fc47..db24e0d 100644
--- a/tempest/api/network/test_load_balancer.py
+++ b/tempest/api/network/test_load_balancer.py
@@ -259,7 +259,7 @@
self.assertEqual('200', resp['status'])
member = body['member']
for key, value in member.iteritems():
- # 'status' should not be confirmed in api tests
+ # 'status' should not be confirmed in api tests
if key != 'status':
self.assertEqual(self.member[key], value)
@@ -340,7 +340,7 @@
self.assertEqual('200', resp['status'])
health_monitor = body['health_monitor']
for key, value in health_monitor.iteritems():
- # 'status' should not be confirmed in api tests
+ # 'status' should not be confirmed in api tests
if key != 'status':
self.assertEqual(self.health_monitor[key], value)
diff --git a/tempest/api/network/test_routers.py b/tempest/api/network/test_routers.py
index 7605b8a..d38633f 100644
--- a/tempest/api/network/test_routers.py
+++ b/tempest/api/network/test_routers.py
@@ -300,9 +300,13 @@
@test.attr(type='smoke')
def test_add_multiple_router_interfaces(self):
- network = self.create_network()
- subnet01 = self.create_subnet(network)
- subnet02 = self.create_subnet(network)
+ network01 = self.create_network(
+ network_name=data_utils.rand_name('router-network01-'))
+ network02 = self.create_network(
+ network_name=data_utils.rand_name('router-network02-'))
+ subnet01 = self.create_subnet(network01)
+ sub02_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr).next()
+ subnet02 = self.create_subnet(network02, cidr=sub02_cidr)
router = self._create_router(data_utils.rand_name('router-'))
interface01 = self._add_router_interface_with_subnet_id(router['id'],
subnet01['id'])
@@ -313,15 +317,6 @@
self._verify_router_interface(router['id'], subnet02['id'],
interface02['port_id'])
- def _add_router_interface_with_subnet_id(self, router_id, subnet_id):
- resp, interface = self.client.add_router_interface_with_subnet_id(
- router_id, subnet_id)
- self.assertEqual('200', resp['status'])
- self.addCleanup(self._remove_router_interface_with_subnet_id,
- router_id, subnet_id)
- self.assertEqual(subnet_id, interface['subnet_id'])
- return interface
-
def _verify_router_interface(self, router_id, subnet_id, port_id):
resp, show_port_body = self.client.show_port(port_id)
self.assertEqual('200', resp['status'])
diff --git a/tempest/api/network/test_routers_negative.py b/tempest/api/network/test_routers_negative.py
index 91ab9d6..feee51b 100644
--- a/tempest/api/network/test_routers_negative.py
+++ b/tempest/api/network/test_routers_negative.py
@@ -13,11 +13,16 @@
# License for the specific language governing permissions and limitations
# under the License.
+import netaddr
+
from tempest.api.network import base_routers as base
from tempest.common.utils import data_utils
+from tempest import config
from tempest import exceptions
from tempest import test
+CONF = config.CONF
+
class RoutersNegativeTest(base.BaseRouterTest):
_interface = 'json'
@@ -43,12 +48,30 @@
@test.attr(type=['negative', 'smoke'])
def test_router_add_gateway_net_not_external_returns_400(self):
- self.create_subnet(self.network)
+ alt_network = self.create_network(
+ network_name=data_utils.rand_name('router-negative-'))
+ sub_cidr = netaddr.IPNetwork(CONF.network.tenant_network_cidr).next()
+ self.create_subnet(alt_network, cidr=sub_cidr)
self.assertRaises(exceptions.BadRequest,
self.client.update_router,
self.router['id'],
external_gateway_info={
- 'network_id': self.network['id']})
+ 'network_id': alt_network['id']})
+
+ @test.attr(type=['negative', 'smoke'])
+ def test_add_router_interfaces_on_overlapping_subnets_returns_400(self):
+ network01 = self.create_network(
+ network_name=data_utils.rand_name('router-network01-'))
+ network02 = self.create_network(
+ network_name=data_utils.rand_name('router-network02-'))
+ subnet01 = self.create_subnet(network01)
+ subnet02 = self.create_subnet(network02)
+ self._add_router_interface_with_subnet_id(self.router['id'],
+ subnet01['id'])
+ self.assertRaises(exceptions.BadRequest,
+ self._add_router_interface_with_subnet_id,
+ self.router['id'],
+ subnet02['id'])
@test.attr(type=['negative', 'smoke'])
def test_router_remove_interface_in_use_returns_409(self):
diff --git a/tempest/api/network/test_security_groups_negative.py b/tempest/api/network/test_security_groups_negative.py
index 0b86398..53c9d12 100644
--- a/tempest/api/network/test_security_groups_negative.py
+++ b/tempest/api/network/test_security_groups_negative.py
@@ -55,7 +55,7 @@
def test_create_security_group_rule_with_bad_protocol(self):
group_create_body, _ = self._create_security_group()
- #Create rule with bad protocol name
+ # Create rule with bad protocol name
pname = 'bad_protocol_name'
self.assertRaises(
exceptions.BadRequest, self.client.create_security_group_rule,
@@ -66,7 +66,7 @@
def test_create_security_group_rule_with_invalid_ports(self):
group_create_body, _ = self._create_security_group()
- #Create rule with invalid ports
+ # Create rule with invalid ports
states = [(-16, 80, 'Invalid value for port -16'),
(80, 79, 'port_range_min must be <= port_range_max'),
(80, 65536, 'Invalid value for port 65536'),
diff --git a/tempest/api/network/test_vpnaas_extensions.py b/tempest/api/network/test_vpnaas_extensions.py
index a49e944..d1fe15c 100644
--- a/tempest/api/network/test_vpnaas_extensions.py
+++ b/tempest/api/network/test_vpnaas_extensions.py
@@ -16,6 +16,7 @@
from tempest.api.network import base
from tempest.common.utils import data_utils
from tempest import config
+from tempest import exceptions
from tempest import test
CONF = config.CONF
@@ -53,6 +54,8 @@
cls.router['id'])
cls.ikepolicy = cls.create_ikepolicy(
data_utils.rand_name("ike-policy-"))
+ cls.ipsecpolicy = cls.create_ipsecpolicy(
+ data_utils.rand_name("ipsec-policy-"))
def _delete_ike_policy(self, ike_policy_id):
# Deletes a ike policy and verifies if it is deleted or not
@@ -70,6 +73,20 @@
ike_id_list.append(i['id'])
self.assertNotIn(ike_policy_id, ike_id_list)
+ def _delete_ipsec_policy(self, ipsec_policy_id):
+ # Deletes an ike policy if it exists
+ try:
+ self.client.delete_ipsecpolicy(ipsec_policy_id)
+
+ except exceptions.NotFound:
+ pass
+
+ def _assertExpected(self, expected, actual):
+ # Check if not expected keys/values exists in actual response body
+ for key, value in expected.iteritems():
+ self.assertIn(key, actual)
+ self.assertEqual(value, actual[key])
+
@test.attr(type='smoke')
def test_list_vpn_services(self):
# Verify the VPN service exists in the list of all VPN services
@@ -177,6 +194,51 @@
self.assertEqual(self.ikepolicy['ike_version'],
ikepolicy['ike_version'])
+ @test.attr(type='smoke')
+ def test_list_ipsec_policies(self):
+ # Verify the ipsec policy exists in the list of all ipsec policies
+ resp, body = self.client.list_ipsecpolicies()
+ self.assertEqual('200', resp['status'])
+ ipsecpolicies = body['ipsecpolicies']
+ self.assertIn(self.ipsecpolicy['id'], [i['id'] for i in ipsecpolicies])
+
+ @test.attr(type='smoke')
+ def test_create_update_delete_ipsec_policy(self):
+ # Creates an ipsec policy
+ ipsec_policy_body = {'name': data_utils.rand_name('ipsec-policy'),
+ 'pfs': 'group5',
+ 'encryption_algorithm': "aes-128",
+ 'auth_algorithm': 'sha1'}
+ resp, resp_body = self.client.create_ipsecpolicy(**ipsec_policy_body)
+ self.assertEqual('201', resp['status'])
+ ipsecpolicy = resp_body['ipsecpolicy']
+ self.addCleanup(self._delete_ipsec_policy, ipsecpolicy['id'])
+ self._assertExpected(ipsec_policy_body, ipsecpolicy)
+ # Verification of ipsec policy update
+ new_ipsec = {'description': 'Updated ipsec policy',
+ 'pfs': 'group2',
+ 'name': data_utils.rand_name("New-IPSec"),
+ 'encryption_algorithm': "aes-256",
+ 'lifetime': {'units': "seconds", 'value': '2000'}}
+ resp, body = self.client.update_ipsecpolicy(ipsecpolicy['id'],
+ **new_ipsec)
+ self.assertEqual('200', resp['status'])
+ updated_ipsec_policy = body['ipsecpolicy']
+ self._assertExpected(new_ipsec, updated_ipsec_policy)
+ # Verification of ipsec policy delete
+ resp, _ = self.client.delete_ipsecpolicy(ipsecpolicy['id'])
+ self.assertEqual('204', resp['status'])
+ self.assertRaises(exceptions.NotFound,
+ self.client.delete_ipsecpolicy, ipsecpolicy['id'])
+
+ @test.attr(type='smoke')
+ def test_show_ipsec_policy(self):
+ # Verifies the details of an ipsec policy
+ resp, body = self.client.show_ipsecpolicy(self.ipsecpolicy['id'])
+ self.assertEqual('200', resp['status'])
+ ipsecpolicy = body['ipsecpolicy']
+ self._assertExpected(self.ipsecpolicy, ipsecpolicy)
+
class VPNaaSTestXML(VPNaaSTestJSON):
_interface = 'xml'
diff --git a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
index cb70d07..a81a540 100644
--- a/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
+++ b/tempest/api/orchestration/stacks/test_nova_keypair_resources.py
@@ -68,13 +68,13 @@
output_map = {}
for outputs in stack['outputs']:
output_map[outputs['output_key']] = outputs['output_value']
- #Test that first key generated public and private keys
+ # Test that first key generated public and private keys
self.assertTrue('KeyPair_PublicKey' in output_map)
self.assertTrue("Generated" in output_map['KeyPair_PublicKey'])
self.assertTrue('KeyPair_PrivateKey' in output_map)
self.assertTrue('-----BEGIN' in output_map['KeyPair_PrivateKey'])
- #Test that second key generated public key, and private key is not
- #in the output due to save_private_key = false
+ # Test that second key generated public key, and private key is not
+ # in the output due to save_private_key = false
self.assertTrue('KeyPairDontSavePrivate_PublicKey' in output_map)
self.assertTrue('Generated' in
output_map['KeyPairDontSavePrivate_PublicKey'])
diff --git a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
index d3a052e..da421dc 100644
--- a/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
+++ b/tempest/api/volume/admin/test_volume_types_extra_specs_negative.py
@@ -29,9 +29,9 @@
super(ExtraSpecsNegativeTest, cls).setUpClass()
vol_type_name = data_utils.rand_name('Volume-type-')
cls.extra_specs = {"spec1": "val1"}
- resp, cls.volume_type = cls.client.create_volume_type(vol_type_name,
- extra_specs=
- cls.extra_specs)
+ resp, cls.volume_type = cls.client.create_volume_type(
+ vol_type_name,
+ extra_specs=cls.extra_specs)
@classmethod
def tearDownClass(cls):
diff --git a/tempest/api/volume/test_volumes_get.py b/tempest/api/volume/test_volumes_get.py
index 58da440..2745b95 100644
--- a/tempest/api/volume/test_volumes_get.py
+++ b/tempest/api/volume/test_volumes_get.py
@@ -39,7 +39,7 @@
def _is_true(self, val):
# NOTE(jdg): Temporary conversion method to get cinder patch
# merged. Then we'll make this strict again and
- #specifically check "true" or "false"
+ # specifically check "true" or "false"
if val in ['true', 'True', True]:
return True
else:
@@ -121,19 +121,19 @@
new_volume = {}
new_v_desc = data_utils.rand_name('@#$%^* description')
resp, new_volume = \
- self.client.create_volume(size=1,
- display_description=new_v_desc,
- availability_zone=
- volume['availability_zone'])
+ self.client.create_volume(
+ size=1,
+ display_description=new_v_desc,
+ availability_zone=volume['availability_zone'])
self.assertEqual(200, resp.status)
self.assertIn('id', new_volume)
self.addCleanup(self._delete_volume, new_volume['id'])
self.client.wait_for_volume_status(new_volume['id'], 'available')
resp, update_volume = \
- self.client.update_volume(new_volume['id'],
- display_name=volume['display_name'],
- display_description=
- volume['display_description'])
+ self.client.update_volume(
+ new_volume['id'],
+ display_name=volume['display_name'],
+ display_description=volume['display_description'])
self.assertEqual(200, resp.status)
# NOTE(jdg): Revert back to strict true/false checking
diff --git a/tempest/api_schema/compute/servers.py b/tempest/api_schema/compute/servers.py
index 4e8c201..14e9ce9 100644
--- a/tempest/api_schema/compute/servers.py
+++ b/tempest/api_schema/compute/servers.py
@@ -92,7 +92,8 @@
'user_id', 'tenant_id', 'created', 'updated',
'metadata', 'links', 'addresses']
}
- }
+ },
+ 'required': ['server']
}
}
@@ -164,3 +165,17 @@
'required': ['output']
}
}
+
+common_instance_actions = {
+ 'type': 'object',
+ 'properties': {
+ 'action': {'type': 'string'},
+ 'request_id': {'type': 'string'},
+ 'user_id': {'type': 'string'},
+ 'project_id': {'type': 'string'},
+ 'start_time': {'type': 'string'},
+ 'message': {'type': ['string', 'null']}
+ },
+ 'required': ['action', 'request_id', 'user_id', 'project_id',
+ 'start_time', 'message']
+}
diff --git a/tempest/api_schema/compute/v2/servers.py b/tempest/api_schema/compute/v2/servers.py
index 981d8f7..fe53abd 100644
--- a/tempest/api_schema/compute/v2/servers.py
+++ b/tempest/api_schema/compute/v2/servers.py
@@ -142,3 +142,57 @@
'required': ['addresses']
}
}
+
+common_server_group = {
+ 'type': 'object',
+ 'properties': {
+ 'id': {'type': 'string'},
+ 'name': {'type': 'string'},
+ 'policies': {
+ 'type': 'array',
+ 'items': {'type': 'string'}
+ },
+ # 'members' attribute contains the array of instance's UUID of
+ # instances present in server group
+ 'members': {
+ 'type': 'array',
+ 'items': {'type': 'string'}
+ },
+ 'metadata': {'type': 'object'}
+ },
+ 'required': ['id', 'name', 'policies', 'members', 'metadata']
+}
+
+create_get_server_group = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'server_group': common_server_group
+ },
+ 'required': ['server_group']
+ }
+}
+
+delete_server_group = {
+ 'status_code': [204]
+}
+
+instance_actions_object = copy.deepcopy(servers.common_instance_actions)
+instance_actions_object[
+ 'properties'].update({'instance_uuid': {'type': 'string'}})
+instance_actions_object['required'].extend(['instance_uuid'])
+
+list_instance_actions = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'instanceActions': {
+ 'type': 'array',
+ 'items': instance_actions_object
+ }
+ },
+ 'required': ['instanceActions']
+ }
+}
diff --git a/tempest/api_schema/compute/v3/servers.py b/tempest/api_schema/compute/v3/servers.py
index 6716249..4fb2d87 100644
--- a/tempest/api_schema/compute/v3/servers.py
+++ b/tempest/api_schema/compute/v3/servers.py
@@ -99,3 +99,21 @@
update_server_metadata = copy.deepcopy(servers.update_server_metadata)
# V3 API's response status_code is 201
update_server_metadata['status_code'] = [201]
+
+server_actions_object = copy.deepcopy(servers.common_instance_actions)
+server_actions_object['properties'].update({'server_uuid': {'type': 'string'}})
+server_actions_object['required'].extend(['server_uuid'])
+
+list_server_actions = {
+ 'status_code': [200],
+ 'response_body': {
+ 'type': 'object',
+ 'properties': {
+ 'server_actions': {
+ 'type': 'array',
+ 'items': server_actions_object
+ }
+ },
+ 'required': ['server_actions']
+ }
+}
diff --git a/tempest/cli/simple_read_only/test_cinder.py b/tempest/cli/simple_read_only/test_cinder.py
index 723333b..e9a0cee 100644
--- a/tempest/cli/simple_read_only/test_cinder.py
+++ b/tempest/cli/simple_read_only/test_cinder.py
@@ -118,6 +118,12 @@
def test_cinder_bash_completion(self):
self.cinder('bash-completion')
+ def test_cinder_qos_list(self):
+ self.cinder('qos-list')
+
+ def test_cinder_encryption_type_list(self):
+ self.cinder('encryption-type-list')
+
def test_admin_help(self):
help_text = self.cinder('help')
lines = help_text.split('\n')
@@ -136,7 +142,7 @@
'quota-show', 'type-list', 'snapshot-list'))
self.assertFalse(wanted_commands - commands)
- # Optional arguments:
+ # Optional arguments:
def test_cinder_version(self):
self.cinder('', flags='--version')
diff --git a/tempest/cli/simple_read_only/test_keystone.py b/tempest/cli/simple_read_only/test_keystone.py
index 1efbede..dda65c1 100644
--- a/tempest/cli/simple_read_only/test_keystone.py
+++ b/tempest/cli/simple_read_only/test_keystone.py
@@ -117,6 +117,11 @@
def test_admin_bashcompletion(self):
self.keystone('bash-completion')
+ def test_admin_ec2_credentials_list(self):
+ creds = self.keystone('ec2-credentials-list')
+ creds = self.parser.listing(creds)
+ self.assertTableStruct(creds, ['tenant', 'access', 'secret'])
+
# Optional arguments:
def test_admin_version(self):
diff --git a/tempest/cmd/run_stress.py b/tempest/cmd/run_stress.py
index f773996..07f3f66 100755
--- a/tempest/cmd/run_stress.py
+++ b/tempest/cmd/run_stress.py
@@ -51,7 +51,7 @@
except Exception:
next
if 'stress' in attrs:
- if filter_attr is not None and not filter_attr in attrs:
+ if filter_attr is not None and filter_attr not in attrs:
continue
class_setup_per = getattr(test_func, "st_class_setup_per")
diff --git a/tempest/cmd/verify_tempest_config.py b/tempest/cmd/verify_tempest_config.py
index 7b2e60b..0834cff 100755
--- a/tempest/cmd/verify_tempest_config.py
+++ b/tempest/cmd/verify_tempest_config.py
@@ -21,7 +21,7 @@
import urlparse
import httplib2
-from six.moves import configparser
+from six import moves
from tempest import clients
from tempest import config
@@ -46,7 +46,7 @@
def change_option(option, group, value):
- config_parse = configparser.SafeConfigParser()
+ config_parse = moves.configparser.SafeConfigParser()
config_parse.optionxform = str
config_parse.readfp(CONF_FILE)
if not config_parse.has_section(group):
@@ -160,11 +160,10 @@
extensions_client = get_extension_client(os, service)
__, resp = extensions_client.list_extensions()
if isinstance(resp, dict):
- # Neutron's extension 'name' field has is not a single word (it has
- # spaces in the string) Since that can't be used for list option the
- # api_extension option in the network-feature-enabled group uses alias
- # instead of name.
- if service == 'neutron':
+ # For both Nova and Neutron we use the alias name rather than the
+ # 'name' field because the alias is considered to be the canonical
+ # name.
+ if service in ['nova', 'nova_v3', 'neutron']:
extensions = map(lambda x: x['alias'], resp['extensions'])
elif service == 'swift':
# Remove Swift general information from extensions list
diff --git a/tempest/common/glance_http.py b/tempest/common/glance_http.py
index 9358851..55aca5a 100644
--- a/tempest/common/glance_http.py
+++ b/tempest/common/glance_http.py
@@ -160,6 +160,9 @@
def json_request(self, method, url, **kwargs):
kwargs.setdefault('headers', {})
kwargs['headers'].setdefault('Content-Type', 'application/json')
+ if kwargs['headers']['Content-Type'] != 'application/json':
+ msg = "Only application/json content-type is supported."
+ raise exc.InvalidContentType(msg)
if 'body' in kwargs:
kwargs['body'] = json.dumps(kwargs['body'])
@@ -173,7 +176,8 @@
except ValueError:
LOG.error('Could not decode response body as JSON')
else:
- body = None
+ msg = "Only json/application content-type is supported."
+ raise exc.InvalidContentType(msg)
return resp, body
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 3c527f5..33128a9 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -191,17 +191,25 @@
"""
self._skip_path = False
- def expected_success(self, expected_code, read_code):
+ @classmethod
+ def expected_success(cls, expected_code, read_code):
assert_msg = ("This function only allowed to use for HTTP status"
"codes which explicitly defined in the RFC 2616. {0}"
" is not a defined Success Code!").format(expected_code)
- assert expected_code in HTTP_SUCCESS, assert_msg
+ if isinstance(expected_code, list):
+ for code in expected_code:
+ assert code in HTTP_SUCCESS, assert_msg
+ else:
+ assert expected_code in HTTP_SUCCESS, assert_msg
# NOTE(afazekas): the http status code above 400 is processed by
# the _error_checker method
- if read_code < 400 and read_code != expected_code:
- pattern = """Unexpected http success status code {0},
- The expected status code is {1}"""
+ if read_code < 400:
+ pattern = """Unexpected http success status code {0},
+ The expected status code is {1}"""
+ if ((not isinstance(expected_code, list) and
+ (read_code != expected_code)) or (isinstance(expected_code,
+ list) and (read_code not in expected_code))):
details = pattern.format(read_code, expected_code)
raise exceptions.InvalidHttpSuccessCode(details)
@@ -555,11 +563,7 @@
# declared in the V3 API and so we should be able to export this in
# the response schema. For now we'll ignore it.
if resp.status in HTTP_SUCCESS:
- response_code = schema['status_code']
- if resp.status not in response_code:
- msg = ("The status code(%s) is different than the expected "
- "one(%s)") % (resp.status, response_code)
- raise exceptions.InvalidHttpSuccessCode(msg)
+ cls.expected_success(schema['status_code'], resp.status)
# Check the body of a response
body_schema = schema.get('response_body')
diff --git a/tempest/common/utils/linux/remote_client.py b/tempest/common/utils/linux/remote_client.py
index 95b6833..57a14a2 100644
--- a/tempest/common/utils/linux/remote_client.py
+++ b/tempest/common/utils/linux/remote_client.py
@@ -57,11 +57,6 @@
actual_hostname = self.exec_command("hostname").rstrip()
return expected_hostname == actual_hostname
- def get_files(self, path):
- # Return a list of comma separated files
- command = "ls -m " + path
- return self.exec_command(command).rstrip('\n').split(', ')
-
def get_ram_size_in_mb(self):
output = self.exec_command('free -m | grep Mem')
if output:
diff --git a/tempest/config.py b/tempest/config.py
index 6d9fda6..e3f0f2a 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -247,7 +247,11 @@
'for removing from a host. -1 never offload, 0 offload '
'when shelved. This time should be the same as the time '
'of nova.conf, and some tests will run for as long as the '
- 'time.')
+ 'time.'),
+ cfg.StrOpt('floating_ip_range',
+ default='10.0.0.0/29',
+ help='Unallocated floating IP range, which will be used to '
+ 'test the floating IP bulk feature for CRUD operation.')
]
compute_features_group = cfg.OptGroup(name='compute-feature-enabled',
@@ -255,7 +259,7 @@
ComputeFeaturesGroup = [
cfg.BoolOpt('api_v3',
- default=True,
+ default=False,
help="If false, skip all nova v3 tests."),
cfg.BoolOpt('disk_config',
default=True,
@@ -263,11 +267,13 @@
cfg.ListOpt('api_extensions',
default=['all'],
help='A list of enabled compute extensions with a special '
- 'entry all which indicates every extension is enabled'),
+ 'entry all which indicates every extension is enabled. '
+ 'Each extension should be specified with alias name'),
cfg.ListOpt('api_v3_extensions',
default=['all'],
help='A list of enabled v3 extensions with a special entry all'
- ' which indicates every extension is enabled'),
+ ' which indicates every extension is enabled. '
+ 'Each extension should be specified with alias name'),
cfg.BoolOpt('change_password',
default=False,
help="Does the test environment support changing the admin "
diff --git a/tempest/scenario/manager.py b/tempest/scenario/manager.py
index 7703d4d..db26bda 100644
--- a/tempest/scenario/manager.py
+++ b/tempest/scenario/manager.py
@@ -289,6 +289,23 @@
rules.append(sg_rule)
return rules
+ def _create_security_group_nova(self, client=None,
+ namestart='secgroup-smoke-'):
+ if client is None:
+ client = self.compute_client
+ # Create security group
+ sg_name = data_utils.rand_name(namestart)
+ sg_desc = sg_name + " description"
+ secgroup = client.security_groups.create(sg_name, sg_desc)
+ self.assertEqual(secgroup.name, sg_name)
+ self.assertEqual(secgroup.description, sg_desc)
+ self.set_resource(sg_name, secgroup)
+
+ # Add rules to the security group
+ self._create_loginable_secgroup_rule_nova(client, secgroup.id)
+
+ return secgroup
+
def create_server(self, client=None, name=None, image=None, flavor=None,
wait=True, create_kwargs={}):
if client is None:
@@ -922,24 +939,6 @@
CONF.compute.ping_timeout,
1)
- def _create_security_group_nova(self, client=None,
- namestart='secgroup-smoke-',
- tenant_id=None):
- if client is None:
- client = self.compute_client
- # Create security group
- sg_name = data_utils.rand_name(namestart)
- sg_desc = sg_name + " description"
- secgroup = client.security_groups.create(sg_name, sg_desc)
- self.assertEqual(secgroup.name, sg_name)
- self.assertEqual(secgroup.description, sg_desc)
- self.set_resource(sg_name, secgroup)
-
- # Add rules to the security group
- self._create_loginable_secgroup_rule_nova(client, secgroup.id)
-
- return secgroup
-
def _create_security_group_neutron(self, tenant_id, client=None,
namestart='secgroup-smoke-'):
if client is None:
diff --git a/tempest/scenario/test_baremetal_basic_ops.py b/tempest/scenario/test_baremetal_basic_ops.py
index 82c6b5d..f197c15 100644
--- a/tempest/scenario/test_baremetal_basic_ops.py
+++ b/tempest/scenario/test_baremetal_basic_ops.py
@@ -31,8 +31,6 @@
* Boots an instance using the keypair
* Monitors the associated Ironic node for power and
expected state transitions
- * Validates Ironic node's driver_info has been properly
- updated
* Validates Ironic node's port data has been properly updated
* Verifies SSH connectivity using created keypair via fixed IP
* Associates a floating ip
@@ -46,17 +44,6 @@
self.instance.add_floating_ip(floating_ip)
return floating_ip.ip
- def validate_driver_info(self):
- f_id = self.instance.flavor['id']
- flavor_extra = self.compute_client.flavors.get(f_id).get_keys()
- driver_info = self.node.driver_info
- self.assertEqual(driver_info['pxe_deploy_kernel'],
- flavor_extra['baremetal:deploy_kernel_id'])
- self.assertEqual(driver_info['pxe_deploy_ramdisk'],
- flavor_extra['baremetal:deploy_ramdisk_id'])
- self.assertEqual(driver_info['pxe_image_source'],
- self.instance.image['id'])
-
def validate_ports(self):
for port in self.get_ports(self.node.uuid):
n_port_id = port.extra['vif_port_id']
@@ -68,7 +55,6 @@
def test_baremetal_server_ops(self):
self.add_keypair()
self.boot_instance()
- self.validate_driver_info()
self.validate_ports()
self.verify_connectivity()
floating_ip = self.add_floating_ip()
diff --git a/tempest/scenario/test_minimum_basic.py b/tempest/scenario/test_minimum_basic.py
index 24d2677..58f0dbf 100644
--- a/tempest/scenario/test_minimum_basic.py
+++ b/tempest/scenario/test_minimum_basic.py
@@ -112,6 +112,11 @@
volume = self.volume_client.volumes.get(self.volume.id)
self.assertEqual('available', volume.status)
+ def create_and_add_security_group(self):
+ secgroup = self._create_security_group_nova()
+ self.server.add_security_group(secgroup.name)
+ self.addCleanup(self.server.remove_security_group, secgroup.name)
+
@test.services('compute', 'volume', 'image', 'network')
def test_minimum_basic_scenario(self):
self.glance_image_create()
@@ -128,7 +133,7 @@
self.nova_floating_ip_create()
self.nova_floating_ip_add()
- self._create_loginable_secgroup_rule_nova()
+ self.create_and_add_security_group()
self.ssh_to_server()
self.nova_reboot()
self.ssh_to_server()
diff --git a/tempest/scenario/test_network_basic_ops.py b/tempest/scenario/test_network_basic_ops.py
index 21782ee..c84d4b9 100644
--- a/tempest/scenario/test_network_basic_ops.py
+++ b/tempest/scenario/test_network_basic_ops.py
@@ -87,6 +87,8 @@
@classmethod
def setUpClass(cls):
+ # Create no network resources for these tests.
+ cls.set_network_resources()
super(TestNetworkBasicOps, cls).setUpClass()
for ext in ['router', 'security-group']:
if not test.is_extension_enabled(ext, 'network'):
diff --git a/tempest/scenario/test_security_groups_basic_ops.py b/tempest/scenario/test_security_groups_basic_ops.py
index 4616b82..b4e509a 100644
--- a/tempest/scenario/test_security_groups_basic_ops.py
+++ b/tempest/scenario/test_security_groups_basic_ops.py
@@ -134,6 +134,8 @@
@classmethod
def setUpClass(cls):
+ # Create no network resources for these tests.
+ cls.set_network_resources()
super(TestSecurityGroupsBasicOps, cls).setUpClass()
cls.alt_creds = cls.alt_credentials()
cls.alt_manager = clients.OfficialClientManager(cls.alt_creds)
diff --git a/tempest/scenario/test_server_basic_ops.py b/tempest/scenario/test_server_basic_ops.py
index 13e00a5..dc7a092 100644
--- a/tempest/scenario/test_server_basic_ops.py
+++ b/tempest/scenario/test_server_basic_ops.py
@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
-from tempest.common.utils import data_utils
from tempest import config
from tempest.openstack.common import log as logging
from tempest.scenario import manager
@@ -68,22 +67,12 @@
def add_keypair(self):
self.keypair = self.create_keypair()
- def create_security_group(self):
- sg_name = data_utils.rand_name('secgroup-smoke')
- sg_desc = sg_name + " description"
- self.secgroup = self.compute_client.security_groups.create(sg_name,
- sg_desc)
- self.assertEqual(self.secgroup.name, sg_name)
- self.assertEqual(self.secgroup.description, sg_desc)
- self.set_resource('secgroup', self.secgroup)
-
- # Add rules to the security group
- self._create_loginable_secgroup_rule_nova(secgroup_id=self.secgroup.id)
-
def boot_instance(self):
# Create server with image and flavor from input scenario
+ security_groups = [self.security_group.name]
create_kwargs = {
- 'key_name': self.keypair.id
+ 'key_name': self.keypair.id,
+ 'security_groups': security_groups
}
instance = self.create_server(image=self.image_ref,
flavor=self.flavor_ref,
@@ -117,7 +106,7 @@
@test.services('compute', 'network')
def test_server_basicops(self):
self.add_keypair()
- self.create_security_group()
+ self.security_group = self._create_security_group_nova()
self.boot_instance()
self.verify_ssh()
self.terminate_instance()
diff --git a/tempest/scenario/test_snapshot_pattern.py b/tempest/scenario/test_snapshot_pattern.py
index 562020a..ab335e2 100644
--- a/tempest/scenario/test_snapshot_pattern.py
+++ b/tempest/scenario/test_snapshot_pattern.py
@@ -35,8 +35,10 @@
"""
def _boot_image(self, image_id):
+ security_groups = [self.security_group.name]
create_kwargs = {
- 'key_name': self.keypair.name
+ 'key_name': self.keypair.name,
+ 'security_groups': security_groups
}
return self.create_server(image=image_id, create_kwargs=create_kwargs)
@@ -72,7 +74,7 @@
def test_snapshot_pattern(self):
# prepare for booting a instance
self._add_keypair()
- self._create_loginable_secgroup_rule_nova()
+ self.security_group = self._create_security_group_nova()
# boot a instance and create a timestamp file in it
server = self._boot_image(CONF.compute.image_ref)
diff --git a/tempest/scenario/test_stamp_pattern.py b/tempest/scenario/test_stamp_pattern.py
index 5235871..20561ae 100644
--- a/tempest/scenario/test_stamp_pattern.py
+++ b/tempest/scenario/test_stamp_pattern.py
@@ -62,8 +62,10 @@
volume_snapshot.id, status)
def _boot_image(self, image_id):
+ security_groups = [self.security_group.name]
create_kwargs = {
- 'key_name': self.keypair.name
+ 'key_name': self.keypair.name,
+ 'security_groups': security_groups
}
return self.create_server(image=image_id, create_kwargs=create_kwargs)
@@ -152,7 +154,7 @@
def test_stamp_pattern(self):
# prepare for booting a instance
self._add_keypair()
- self._create_loginable_secgroup_rule_nova()
+ self.security_group = self._create_security_group_nova()
# boot an instance and create a timestamp file in it
volume = self._create_volume()
diff --git a/tempest/scenario/test_volume_boot_pattern.py b/tempest/scenario/test_volume_boot_pattern.py
index faca31f..4905dbf 100644
--- a/tempest/scenario/test_volume_boot_pattern.py
+++ b/tempest/scenario/test_volume_boot_pattern.py
@@ -55,9 +55,11 @@
bd_map = {
'vda': vol_id + ':::0'
}
+ security_groups = [self.security_group.name]
create_kwargs = {
'block_device_mapping': bd_map,
- 'key_name': keypair.name
+ 'key_name': keypair.name,
+ 'security_groups': security_groups
}
return self.create_server(image='', create_kwargs=create_kwargs)
@@ -135,7 +137,7 @@
@test.services('compute', 'volume', 'image')
def test_volume_boot_pattern(self):
keypair = self.create_keypair()
- self._create_loginable_secgroup_rule_nova()
+ self.security_group = self._create_security_group_nova()
# create an instance from volume
volume_origin = self._create_volume_from_image()
@@ -182,8 +184,10 @@
bdms = [{'uuid': vol_id, 'source_type': 'volume',
'destination_type': 'volume', 'boot_index': 0,
'delete_on_termination': False}]
+ security_groups = [self.security_group.name]
create_kwargs = {
'block_device_mapping_v2': bdms,
- 'key_name': keypair.name
+ 'key_name': keypair.name,
+ 'security_groups': security_groups
}
return self.create_server(image='', create_kwargs=create_kwargs)
diff --git a/tempest/services/baremetal/base.py b/tempest/services/baremetal/base.py
index 2af287f..321b08b 100644
--- a/tempest/services/baremetal/base.py
+++ b/tempest/services/baremetal/base.py
@@ -199,3 +199,14 @@
"""
return self._list_request(version, permanent=True)
+
+ def _put_request(self, resource, put_object):
+ """
+ Update specified object with JSON-patch.
+
+ """
+ uri = self._get_uri(resource)
+ put_body = json.dumps(put_object)
+
+ resp, body = self.put(uri, body=put_body)
+ return resp, body
diff --git a/tempest/services/baremetal/v1/base_v1.py b/tempest/services/baremetal/v1/base_v1.py
index 296a199..52479b5 100644
--- a/tempest/services/baremetal/v1/base_v1.py
+++ b/tempest/services/baremetal/v1/base_v1.py
@@ -226,3 +226,16 @@
"""
return self._patch_request('ports', uuid, patch)
+
+ @base.handle_errors
+ def set_node_power_state(self, node_uuid, state):
+ """
+ Set power state of the specified node.
+
+ :param node_uuid: The unique identifier of the node.
+ :state: desired state to set (on/off/reboot).
+
+ """
+ target = {'target': state}
+ return self._put_request('nodes/%s/states/power' % node_uuid,
+ target)
diff --git a/tempest/services/compute/json/floating_ips_client.py b/tempest/services/compute/json/floating_ips_client.py
index e2e12d5..6fc2bc2 100644
--- a/tempest/services/compute/json/floating_ips_client.py
+++ b/tempest/services/compute/json/floating_ips_client.py
@@ -112,3 +112,28 @@
body = json.loads(body)
self.validate_response(schema.floating_ip_pools, resp, body)
return resp, body['floating_ip_pools']
+
+ def create_floating_ips_bulk(self, ip_range, pool, interface):
+ """Allocate floating IPs in bulk."""
+ post_body = {
+ 'ip_range': ip_range,
+ 'pool': pool,
+ 'interface': interface
+ }
+ post_body = json.dumps({'floating_ips_bulk_create': post_body})
+ resp, body = self.post('os-floating-ips-bulk', post_body)
+ body = json.loads(body)
+ return resp, body['floating_ips_bulk_create']
+
+ def list_floating_ips_bulk(self):
+ """Returns a list of all floating IPs bulk."""
+ resp, body = self.get('os-floating-ips-bulk')
+ body = json.loads(body)
+ return resp, body['floating_ip_info']
+
+ def delete_floating_ips_bulk(self, ip_range):
+ """Deletes the provided floating IPs bulk."""
+ post_body = json.dumps({'ip_range': ip_range})
+ resp, body = self.put('os-floating-ips-bulk/delete', post_body)
+ body = json.loads(body)
+ return resp, body['floating_ips_bulk_delete']
diff --git a/tempest/services/compute/json/servers_client.py b/tempest/services/compute/json/servers_client.py
index 36bb02f..23c1e64 100644
--- a/tempest/services/compute/json/servers_client.py
+++ b/tempest/services/compute/json/servers_client.py
@@ -57,6 +57,7 @@
max_count: Count of maximum number of instances to launch.
disk_config: Determines if user or admin controls disk configuration.
return_reservation_id: Enable/Disable the return of reservation id
+ block_device_mapping: Block device mapping for the server.
"""
post_body = {
'name': name,
@@ -69,7 +70,7 @@
'availability_zone', 'accessIPv4', 'accessIPv6',
'min_count', 'max_count', ('metadata', 'meta'),
('OS-DCF:diskConfig', 'disk_config'),
- 'return_reservation_id']:
+ 'return_reservation_id', 'block_device_mapping']:
if isinstance(option, tuple):
post_param = option[0]
key = option[1]
@@ -462,6 +463,7 @@
resp, body = self.get("servers/%s/os-instance-actions" %
str(server_id))
body = json.loads(body)
+ self.validate_response(schema.list_instance_actions, resp, body)
return resp, body['instanceActions']
def get_instance_action(self, server_id, request_id):
@@ -508,11 +510,14 @@
resp, body = self.post('os-server-groups', post_body)
body = json.loads(body)
+ self.validate_response(schema.create_get_server_group, resp, body)
return resp, body['server_group']
def delete_server_group(self, server_group_id):
"""Delete the given server-group."""
- return self.delete("os-server-groups/%s" % str(server_group_id))
+ resp, body = self.delete("os-server-groups/%s" % str(server_group_id))
+ self.validate_response(schema.delete_server_group, resp, body)
+ return resp, body
def list_server_groups(self):
"""List the server-groups."""
@@ -524,4 +529,5 @@
"""Get the details of given server_group."""
resp, body = self.get("os-server-groups/%s" % str(server_group_id))
body = json.loads(body)
+ self.validate_response(schema.create_get_server_group, resp, body)
return resp, body['server_group']
diff --git a/tempest/services/compute/v3/json/servers_client.py b/tempest/services/compute/v3/json/servers_client.py
index eed85c7..11258a6 100644
--- a/tempest/services/compute/v3/json/servers_client.py
+++ b/tempest/services/compute/v3/json/servers_client.py
@@ -55,6 +55,7 @@
max_count: Count of maximum number of instances to launch.
disk_config: Determines if user or admin controls disk configuration.
return_reservation_id: Enable/Disable the return of reservation id
+ block_device_mapping: Block device mapping for the server.
"""
post_body = {
'name': name,
@@ -75,7 +76,9 @@
('metadata', 'meta'),
('os-disk-config:disk_config', 'disk_config'),
('os-multiple-create:return_reservation_id',
- 'return_reservation_id')]:
+ 'return_reservation_id'),
+ ('os-block-device-mapping:block_device_mapping',
+ 'block_device_mapping')]:
if isinstance(option, tuple):
post_param = option[0]
key = option[1]
@@ -432,6 +435,9 @@
return self.action(server_id, 'shelve_offload', None, **kwargs)
def get_console_output(self, server_id, length):
+ if length is None:
+ # NOTE(mriedem): -1 means optional/unlimited in the nova v3 API.
+ length = -1
return self.action(server_id, 'get_console_output', 'output',
common_schema.get_console_output, length=length)
@@ -455,6 +461,7 @@
resp, body = self.get("servers/%s/os-server-actions" %
str(server_id))
body = json.loads(body)
+ self.validate_response(schema.list_server_actions, resp, body)
return resp, body['server_actions']
def get_server_action(self, server_id, request_id):
diff --git a/tempest/services/compute/xml/servers_client.py b/tempest/services/compute/xml/servers_client.py
index c1105f9..626e655 100644
--- a/tempest/services/compute/xml/servers_client.py
+++ b/tempest/services/compute/xml/servers_client.py
@@ -318,6 +318,7 @@
min_count: Count of minimum number of instances to launch.
max_count: Count of maximum number of instances to launch.
disk_config: Determines if user or admin controls disk configuration.
+ block_device_mapping: Block device mapping for the server.
"""
server = xml_utils.Element("server",
xmlns=xml_utils.XMLNS_11,
@@ -327,7 +328,8 @@
for attr in ["adminPass", "accessIPv4", "accessIPv6", "key_name",
"user_data", "availability_zone", "min_count",
- "max_count", "return_reservation_id"]:
+ "max_count", "return_reservation_id",
+ "block_device_mapping"]:
if attr in kwargs:
server.add_attr(attr, kwargs[attr])
diff --git a/tempest/services/data_processing/v1_1/client.py b/tempest/services/data_processing/v1_1/client.py
index 4465968..c2c7fd1 100644
--- a/tempest/services/data_processing/v1_1/client.py
+++ b/tempest/services/data_processing/v1_1/client.py
@@ -192,3 +192,43 @@
uri = 'job-binary-internals/%s/data' % job_binary_id
return self.get(uri)
+
+ def list_job_binaries(self):
+ """List all job binaries for a user."""
+
+ uri = 'job-binaries'
+ return self._request_and_parse(self.get, uri, 'binaries')
+
+ def get_job_binary(self, job_binary_id):
+ """Returns the details of a single job binary."""
+
+ uri = 'job-binaries/%s' % job_binary_id
+ return self._request_and_parse(self.get, uri, 'job_binary')
+
+ def create_job_binary(self, name, url, extra=None, **kwargs):
+ """Creates job binary with specified params.
+
+ It supports passing additional params using kwargs and returns created
+ object.
+ """
+ uri = 'job-binaries'
+ body = kwargs.copy()
+ body.update({
+ 'name': name,
+ 'url': url,
+ 'extra': extra or dict(),
+ })
+ return self._request_and_parse(self.post, uri, 'job_binary',
+ body=json.dumps(body))
+
+ def delete_job_binary(self, job_binary_id):
+ """Deletes the specified job binary by id."""
+
+ uri = 'job-binaries/%s' % job_binary_id
+ return self.delete(uri)
+
+ def get_job_binary_data(self, job_binary_id):
+ """Returns data of a single job binary."""
+
+ uri = 'job-binaries/%s/data' % job_binary_id
+ return self.get(uri)
diff --git a/tempest/services/network/network_client_base.py b/tempest/services/network/network_client_base.py
index 2a797b2..81792c4 100644
--- a/tempest/services/network/network_client_base.py
+++ b/tempest/services/network/network_client_base.py
@@ -30,6 +30,7 @@
'members': 'lb',
'vpnservices': 'vpn',
'ikepolicies': 'vpn',
+ 'ipsecpolicies': 'vpn',
'metering_labels': 'metering',
'metering_label_rules': 'metering',
'firewall_rules': 'fw',
@@ -47,6 +48,7 @@
'security_groups': 'security_groups',
'security_group_rules': 'security_group_rules',
'ikepolicy': 'ikepolicies',
+ 'ipsecpolicy': 'ipsecpolicies',
'quotas': 'quotas',
'firewall_policy': 'firewall_policies'
}
diff --git a/tempest/services/network/xml/network_client.py b/tempest/services/network/xml/network_client.py
index a7a6b2c..22cc948 100644
--- a/tempest/services/network/xml/network_client.py
+++ b/tempest/services/network/xml/network_client.py
@@ -38,7 +38,7 @@
return _root_tag_fetcher_and_xml_to_json_parse(body)
def serialize(self, body):
- #TODO(enikanorov): implement better json to xml conversion
+ # TODO(enikanorov): implement better json to xml conversion
# expecting the dict with single key
root = body.keys()[0]
post_body = common.Element(root)
diff --git a/tempest/services/volume/xml/backups_client.py b/tempest/services/volume/xml/backups_client.py
index 81caaee..a691a25 100644
--- a/tempest/services/volume/xml/backups_client.py
+++ b/tempest/services/volume/xml/backups_client.py
@@ -22,5 +22,5 @@
"""
TYPE = "xml"
- #TODO(gfidente): XML client isn't yet implemented because of bug 1270589
+ # TODO(gfidente): XML client isn't yet implemented because of bug 1270589
pass
diff --git a/tempest/stress/actions/volume_attach_delete.py b/tempest/stress/actions/volume_attach_delete.py
index c2e6072..b438f52 100644
--- a/tempest/stress/actions/volume_attach_delete.py
+++ b/tempest/stress/actions/volume_attach_delete.py
@@ -28,9 +28,9 @@
# Step 1: create volume
name = data_utils.rand_name("volume")
self.logger.info("creating volume: %s" % name)
- resp, volume = self.manager.volumes_client.create_volume(size=1,
- display_name=
- name)
+ resp, volume = self.manager.volumes_client.create_volume(
+ size=1,
+ display_name=name)
assert(resp.status == 200)
self.manager.volumes_client.wait_for_volume_status(volume['id'],
'available')
diff --git a/tempest/stress/actions/volume_attach_verify.py b/tempest/stress/actions/volume_attach_verify.py
index 1bc3b06..a3ca0b7 100644
--- a/tempest/stress/actions/volume_attach_verify.py
+++ b/tempest/stress/actions/volume_attach_verify.py
@@ -81,9 +81,9 @@
name = data_utils.rand_name("volume")
self.logger.info("creating volume: %s" % name)
volumes_client = self.manager.volumes_client
- resp, self.volume = volumes_client.create_volume(size=1,
- display_name=
- name)
+ resp, self.volume = volumes_client.create_volume(
+ size=1,
+ display_name=name)
assert(resp.status == 200)
volumes_client.wait_for_volume_status(self.volume['id'],
'available')
diff --git a/tempest/test.py b/tempest/test.py
index 748a98c..bc94bcd 100644
--- a/tempest/test.py
+++ b/tempest/test.py
@@ -396,7 +396,7 @@
:param file: the file name
"""
- #NOTE(mkoderer): must be extended for xml support
+ # NOTE(mkoderer): must be extended for xml support
fn = os.path.join(
os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
"etc", "schemas", file)
diff --git a/tempest/tests/cmd/test_verify_tempest_config.py b/tempest/tests/cmd/test_verify_tempest_config.py
index 40caf30..d0140dd 100644
--- a/tempest/tests/cmd/test_verify_tempest_config.py
+++ b/tempest/tests/cmd/test_verify_tempest_config.py
@@ -15,6 +15,7 @@
import json
import mock
+from oslo.config import cfg
from tempest.cmd import verify_tempest_config
from tempest import config
@@ -152,6 +153,7 @@
False, True)
def test_verify_nova_versions(self):
+ cfg.CONF.set_default('api_v3', True, 'compute-feature-enabled')
self.useFixture(mockpatch.PatchObject(
verify_tempest_config, '_get_unversioned_endpoint',
return_value='http://fake_endpoint:5000'))
@@ -280,9 +282,9 @@
def test_verify_extensions_nova(self):
def fake_list_extensions():
- return (None, {'extensions': [{'name': 'fake1'},
- {'name': 'fake2'},
- {'name': 'not_fake'}]})
+ return (None, {'extensions': [{'alias': 'fake1'},
+ {'alias': 'fake2'},
+ {'alias': 'not_fake'}]})
fake_os = mock.MagicMock()
fake_os.extensions_client.list_extensions = fake_list_extensions
self.useFixture(mockpatch.PatchObject(
@@ -302,9 +304,9 @@
def test_verify_extensions_nova_all(self):
def fake_list_extensions():
- return (None, {'extensions': [{'name': 'fake1'},
- {'name': 'fake2'},
- {'name': 'not_fake'}]})
+ return (None, {'extensions': [{'alias': 'fake1'},
+ {'alias': 'fake2'},
+ {'alias': 'not_fake'}]})
fake_os = mock.MagicMock()
fake_os.extensions_client.list_extensions = fake_list_extensions
self.useFixture(mockpatch.PatchObject(
@@ -319,9 +321,9 @@
def test_verify_extensions_nova_v3(self):
def fake_list_extensions():
- return (None, {'extensions': [{'name': 'fake1'},
- {'name': 'fake2'},
- {'name': 'not_fake'}]})
+ return (None, {'extensions': [{'alias': 'fake1'},
+ {'alias': 'fake2'},
+ {'alias': 'not_fake'}]})
fake_os = mock.MagicMock()
fake_os.extensions_v3_client.list_extensions = fake_list_extensions
self.useFixture(mockpatch.PatchObject(
@@ -341,9 +343,9 @@
def test_verify_extensions_nova_v3_all(self):
def fake_list_extensions():
- return (None, {'extensions': [{'name': 'fake1'},
- {'name': 'fake2'},
- {'name': 'not_fake'}]})
+ return (None, {'extensions': [{'alias': 'fake1'},
+ {'alias': 'fake2'},
+ {'alias': 'not_fake'}]})
fake_os = mock.MagicMock()
fake_os.extensions_v3_client.list_extensions = fake_list_extensions
self.useFixture(mockpatch.PatchObject(
diff --git a/tempest/tests/common/utils/linux/__init__.py b/tempest/tests/common/utils/linux/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tempest/tests/common/utils/linux/__init__.py
diff --git a/tempest/tests/common/utils/linux/test_remote_client.py b/tempest/tests/common/utils/linux/test_remote_client.py
new file mode 100644
index 0000000..0db4cfa
--- /dev/null
+++ b/tempest/tests/common/utils/linux/test_remote_client.py
@@ -0,0 +1,150 @@
+# Copyright 2014 IBM Corp.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import time
+
+from oslo.config import cfg
+
+from tempest.common.utils.linux import remote_client
+from tempest import config
+from tempest.openstack.common.fixture import mockpatch
+from tempest.tests import base
+from tempest.tests import fake_config
+
+
+class TestRemoteClient(base.TestCase):
+ def setUp(self):
+ super(TestRemoteClient, self).setUp()
+ self.useFixture(fake_config.ConfigFixture())
+ self.stubs.Set(config, 'TempestConfigPrivate', fake_config.FakePrivate)
+ cfg.CONF.set_default('ip_version_for_ssh', 4, group='compute')
+ cfg.CONF.set_default('network_for_ssh', 'public', group='compute')
+ cfg.CONF.set_default('ssh_channel_timeout', 1, group='compute')
+
+ self.conn = remote_client.RemoteClient('127.0.0.1', 'user', 'pass')
+ self.ssh_mock = self.useFixture(mockpatch.PatchObject(self.conn,
+ 'ssh_client'))
+
+ def test_hostname_equals_servername_for_expected_names(self):
+ self.ssh_mock.mock.exec_command.return_value = 'fake_hostname'
+ self.assertTrue(self.conn.hostname_equals_servername('fake_hostname'))
+
+ def test_hostname_equals_servername_for_unexpected_names(self):
+ self.ssh_mock.mock.exec_command.return_value = 'fake_hostname'
+ self.assertFalse(
+ self.conn.hostname_equals_servername('unexpected_hostname'))
+
+ def test_get_ram_size(self):
+ free_output = "Mem: 48294 45738 2555 0" \
+ "402 40346"
+ self.ssh_mock.mock.exec_command.return_value = free_output
+ self.assertEqual(self.conn.get_ram_size_in_mb(), '48294')
+
+ def test_write_to_console_regular_str(self):
+ self.conn.write_to_console('test')
+ self._assert_exec_called_with(
+ 'sudo sh -c "echo \\"test\\" >/dev/console"')
+
+ def _test_write_to_console_helper(self, message, expected_call):
+ self.conn.write_to_console(message)
+ self._assert_exec_called_with(expected_call)
+
+ def test_write_to_console_special_chars(self):
+ self._test_write_to_console_helper(
+ '\`',
+ 'sudo sh -c "echo \\"\\\\\\`\\" >/dev/console"')
+ self.conn.write_to_console('$')
+ self._assert_exec_called_with(
+ 'sudo sh -c "echo \\"\\\\$\\" >/dev/console"')
+
+ # NOTE(maurosr): The tests below end up closer to an output format
+ # assurance than a test since it's basically using comand_exec to format
+ # the information using gnu/linux tools.
+
+ def _assert_exec_called_with(self, cmd):
+ self.ssh_mock.mock.exec_command.assert_called_with(cmd)
+
+ def test_get_number_of_vcpus(self):
+ self.ssh_mock.mock.exec_command.return_value = '16'
+ self.assertEqual(self.conn.get_number_of_vcpus(), 16)
+ self._assert_exec_called_with(
+ 'cat /proc/cpuinfo | grep processor | wc -l')
+
+ def test_get_partitions(self):
+ proc_partitions = """major minor #blocks name
+
+8 0 1048576 vda"""
+ self.ssh_mock.mock.exec_command.return_value = proc_partitions
+ self.assertEqual(self.conn.get_partitions(), proc_partitions)
+ self._assert_exec_called_with('cat /proc/partitions')
+
+ def test_get_boot_time(self):
+ booted_at = 10000
+ uptime_sec = 5000.02
+ self.ssh_mock.mock.exec_command.return_value = uptime_sec
+ self.useFixture(mockpatch.PatchObject(
+ time, 'time', return_value=booted_at + uptime_sec))
+ self.assertEqual(self.conn.get_boot_time(),
+ time.localtime(booted_at))
+ self._assert_exec_called_with('cut -f1 -d. /proc/uptime')
+
+ def test_ping_host(self):
+ ping_response = """PING localhost (127.0.0.1) 56(84) bytes of data.
+64 bytes from localhost (127.0.0.1): icmp_req=1 ttl=64 time=0.048 ms
+
+--- localhost ping statistics ---
+1 packets transmitted, 1 received, 0% packet loss, time 0ms
+rtt min/avg/max/mdev = 0.048/0.048/0.048/0.000 ms"""
+ self.ssh_mock.mock.exec_command.return_value = ping_response
+ self.assertEqual(self.conn.ping_host('127.0.0.1'), ping_response)
+ self._assert_exec_called_with('ping -c1 -w1 127.0.0.1')
+
+ def test_get_mac_address(self):
+ macs = """0a:0b:0c:0d:0e:0f
+a0:b0:c0:d0:e0:f0"""
+ self.ssh_mock.mock.exec_command.return_value = macs
+
+ self.assertEqual(self.conn.get_mac_address(), macs)
+ self._assert_exec_called_with(
+ "/sbin/ifconfig | awk '/HWaddr/ {print $5}'")
+
+ def test_get_ip_list(self):
+ ips = """1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue
+ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
+ inet 127.0.0.1/8 scope host lo
+ inet6 ::1/128 scope host
+ valid_lft forever preferred_lft forever
+2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
+ link/ether fa:16:3e:6e:26:3b brd ff:ff:ff:ff:ff:ff
+ inet 10.0.0.4/24 brd 10.0.0.255 scope global eth0
+ inet6 fd55:faaf:e1ab:3d9:f816:3eff:fe6e:263b/64 scope global dynamic
+ valid_lft 2591936sec preferred_lft 604736sec
+ inet6 fe80::f816:3eff:fe6e:263b/64 scope link
+ valid_lft forever preferred_lft forever"""
+ self.ssh_mock.mock.exec_command.return_value = ips
+ self.assertEqual(self.conn.get_ip_list(), ips)
+ self._assert_exec_called_with('/bin/ip address')
+
+ def test_assign_static_ip(self):
+ self.ssh_mock.mock.exec_command.return_value = ''
+ ip = '10.0.0.2'
+ nic = 'eth0'
+ self.assertEqual(self.conn.assign_static_ip(nic, ip), '')
+ self._assert_exec_called_with(
+ "sudo /bin/ip addr add %s/%s dev %s" % (ip, '28', nic))
+
+ def test_turn_nic_on(self):
+ nic = 'eth0'
+ self.conn.turn_nic_on(nic)
+ self._assert_exec_called_with('sudo /bin/ip link set %s up' % nic)
diff --git a/tempest/tests/fake_http.py b/tempest/tests/fake_http.py
index 7b878af..ce2b2c0 100644
--- a/tempest/tests/fake_http.py
+++ b/tempest/tests/fake_http.py
@@ -32,7 +32,6 @@
'headers': headers
}
return (fake_headers, return_obj)
- # return (headers, return_obj)
elif isinstance(self.return_type, int):
body = "fake_body"
header_info = {
diff --git a/tempest/tests/test_glance_http.py b/tempest/tests/test_glance_http.py
index bb2df43..88b8129 100644
--- a/tempest/tests/test_glance_http.py
+++ b/tempest/tests/test_glance_http.py
@@ -54,26 +54,37 @@
return_value=resp))
return resp
- def test_json_request_without_content_type_header(self):
+ def test_json_request_without_content_type_header_in_response(self):
self._set_response_fixture({}, 200, 'fake_response_body')
- resp, body = self.client.json_request('GET', '/images')
- self.assertEqual(200, resp.status)
- self.assertIsNone(body)
+ self.assertRaises(exceptions.InvalidContentType,
+ self.client.json_request, 'GET', '/images')
- def test_json_request_with_xml_content_type_header(self):
+ def test_json_request_with_xml_content_type_header_in_request(self):
+ self.assertRaises(exceptions.InvalidContentType,
+ self.client.json_request, 'GET', '/images',
+ headers={'Content-Type': 'application/xml'})
+
+ def test_json_request_with_xml_content_type_header_in_response(self):
self._set_response_fixture({'content-type': 'application/xml'},
200, 'fake_response_body')
- resp, body = self.client.json_request('GET', '/images')
- self.assertEqual(200, resp.status)
- self.assertIsNone(body)
+ self.assertRaises(exceptions.InvalidContentType,
+ self.client.json_request, 'GET', '/images')
- def test_json_request_with_content_type_header(self):
+ def test_json_request_with_json_content_type_header_only_in_resp(self):
self._set_response_fixture({'content-type': 'application/json'},
200, 'fake_response_body')
resp, body = self.client.json_request('GET', '/images')
self.assertEqual(200, resp.status)
self.assertEqual('fake_response_body', body)
+ def test_json_request_with_json_content_type_header_in_req_and_resp(self):
+ self._set_response_fixture({'content-type': 'application/json'},
+ 200, 'fake_response_body')
+ resp, body = self.client.json_request('GET', '/images', headers={
+ 'Content-Type': 'application/json'})
+ self.assertEqual(200, resp.status)
+ self.assertEqual('fake_response_body', body)
+
def test_json_request_fails_to_json_loads(self):
self._set_response_fixture({'content-type': 'application/json'},
200, 'fake_response_body')
diff --git a/tempest/tests/test_rest_client.py b/tempest/tests/test_rest_client.py
index d20520c..a351bd5 100644
--- a/tempest/tests/test_rest_client.py
+++ b/tempest/tests/test_rest_client.py
@@ -541,3 +541,50 @@
self.assertRaises(AssertionError,
self.negative_rest_client.send_request,
'OTHER', self.url, [])
+
+
+class TestExpectedSuccess(BaseRestClientTestClass):
+
+ def setUp(self):
+ self.fake_http = fake_http.fake_httplib2()
+ super(TestExpectedSuccess, self).setUp()
+
+ def test_expected_succes_int_match(self):
+ expected_code = 202
+ read_code = 202
+ resp = self.rest_client.expected_success(expected_code, read_code)
+ # Assert None resp on success
+ self.assertFalse(resp)
+
+ def test_expected_succes_int_no_match(self):
+ expected_code = 204
+ read_code = 202
+ self.assertRaises(exceptions.InvalidHttpSuccessCode,
+ self.rest_client.expected_success,
+ expected_code, read_code)
+
+ def test_expected_succes_list_match(self):
+ expected_code = [202, 204]
+ read_code = 202
+ resp = self.rest_client.expected_success(expected_code, read_code)
+ # Assert None resp on success
+ self.assertFalse(resp)
+
+ def test_expected_succes_list_no_match(self):
+ expected_code = [202, 204]
+ read_code = 200
+ self.assertRaises(exceptions.InvalidHttpSuccessCode,
+ self.rest_client.expected_success,
+ expected_code, read_code)
+
+ def test_non_success_expected_int(self):
+ expected_code = 404
+ read_code = 202
+ self.assertRaises(AssertionError, self.rest_client.expected_success,
+ expected_code, read_code)
+
+ def test_non_success_expected_list(self):
+ expected_code = [404, 202]
+ read_code = 202
+ self.assertRaises(AssertionError, self.rest_client.expected_success,
+ expected_code, read_code)
diff --git a/tempest/thirdparty/boto/test.py b/tempest/thirdparty/boto/test.py
index 4c39f78..d3cbc4b 100644
--- a/tempest/thirdparty/boto/test.py
+++ b/tempest/thirdparty/boto/test.py
@@ -56,8 +56,8 @@
A_I_IMAGES_READY = all_read(ami_path, aki_path, ari_path)
boto_logger = logging.getLogger('boto')
level = boto_logger.logger.level
- boto_logger.logger.setLevel(orig_logging.CRITICAL) # suppress logging
- # for these
+ # suppress logging for boto
+ boto_logger.logger.setLevel(orig_logging.CRITICAL)
def _cred_sub_check(connection_data):
if not id_matcher.match(connection_data["aws_access_key_id"]):
diff --git a/tempest/thirdparty/boto/test_ec2_instance_run.py b/tempest/thirdparty/boto/test_ec2_instance_run.py
index b2eb18d..e422ffb 100644
--- a/tempest/thirdparty/boto/test_ec2_instance_run.py
+++ b/tempest/thirdparty/boto/test_ec2_instance_run.py
@@ -80,8 +80,8 @@
if state != "available":
for _image in cls.images.itervalues():
cls.ec2_client.deregister_image(_image["image_id"])
- raise exceptions.EC2RegisterImageException(image_id=
- image["image_id"])
+ raise exceptions.EC2RegisterImageException(
+ image_id=image["image_id"])
@test.attr(type='smoke')
def test_run_idempotent_instances(self):
diff --git a/test-requirements.txt b/test-requirements.txt
index b9c75c8..f63d34e 100644
--- a/test-requirements.txt
+++ b/test-requirements.txt
@@ -1,4 +1,4 @@
-hacking>=0.8.0,<0.9
+hacking>=0.9.2,<0.10
# needed for doc build
docutils==0.9.1
sphinx>=1.2.1,<1.3
diff --git a/tools/subunit-trace.py b/tools/subunit-trace.py
index 9bfefe1..c6f8eab 100755
--- a/tools/subunit-trace.py
+++ b/tools/subunit-trace.py
@@ -221,6 +221,14 @@
return count
+def run_time():
+ runtime = 0.0
+ for k, v in RESULTS.items():
+ for test in v:
+ runtime += float(get_duration(test['timestamps']).strip('s'))
+ return runtime
+
+
def worker_stats(worker):
tests = RESULTS[worker]
num_tests = len(tests)
@@ -230,7 +238,8 @@
def print_summary(stream):
stream.write("\n======\nTotals\n======\n")
- stream.write("Run: %s\n" % count_tests('status', '.*'))
+ stream.write("Run: %s in %s sec.\n" % (count_tests('status', '.*'),
+ run_time()))
stream.write(" - Passed: %s\n" % count_tests('status', 'success'))
stream.write(" - Skipped: %s\n" % count_tests('status', 'skip'))
stream.write(" - Failed: %s\n" % count_tests('status', 'fail'))
diff --git a/tox.ini b/tox.ini
index 6b4acc6..c1acde9 100644
--- a/tox.ini
+++ b/tox.ini
@@ -99,6 +99,8 @@
[flake8]
# E125 is a won't fix until https://github.com/jcrocholl/pep8/issues/126 is resolved. For further detail see https://review.openstack.org/#/c/36788/
-ignore = E125,H404
+# H402 skipped because some docstrings aren't sentences
+# Skipped because of new hacking 0.9: H407,H405,H904,H305,E123,H307,E122,E129,E128
+ignore = E125,H402,H404,H407,H405,H904,H305,E123,H307,E122,E129,E128
show-source = True
exclude = .git,.venv,.tox,dist,doc,openstack,*egg