Fix tips jobs and convert Nova Floating IP calls

The script to install the git versions of the libraries wasn't working
because it wasn't installing over top of the versions already installed
by pip. So by adding an uninstall first, we ensure that the git version
is actually installed.

Also, add a pbr freeze to the end so that we can verify, and some +x so
that we can see the output of our scripts.

And: novaclient 8.0 doesn't have these calls anymore. However, the tests we
had on legacy clouds were erroneously installing the wrong version of
novaclient so we did not catch it. Doh. This has to do the tests and the
calls in the same patch because the fix to the gate job to install the
correct verison of the library exposed the fact that we had be broken
for these for a few minutes.

Depends-On: I208e8c009d0438de19cd3eb08dc45ddebb45d3e9
Change-Id: I4fd882aeb8373b94c7f6b54d97b457042b324361
This commit is contained in:
Monty Taylor 2017-04-27 18:46:04 -05:00
parent 33aa7948b6
commit a88d41e302
No known key found for this signature in database
GPG Key ID: 7BAE94BC7141A594
7 changed files with 207 additions and 187 deletions

View File

@ -15,16 +15,21 @@
# limitations under the License. # limitations under the License.
for lib in \ for lib in \
os-client-config \
python-novaclient \ python-novaclient \
python-keystoneclient \ python-keystoneclient \
python-cinderclient \ python-cinderclient \
python-ironicclient \ python-ironicclient \
python-designateclient \ python-designateclient \
os-client-config \
keystoneauth keystoneauth
do do
egg=$(echo $lib | tr '-' '_' | sed 's/python-//') egg=$(echo $lib | tr '-' '_' | sed 's/python-//')
if [ -d /opt/stack/new/$lib ] ; then if [ -d /opt/stack/new/$lib ] ; then
pip install -q -U -e "git+file:///opt/stack/new/$lib#egg=$egg" tip_location="git+file:///opt/stack/new/$lib#egg=$egg"
echo "$(which pip) install -U -e $tip_location"
pip uninstall -y $lib
pip install -U -e $tip_location
else
echo "$lib not found in /opt/stack/new/$lib"
fi fi
done done

View File

@ -212,31 +212,6 @@ class NovaImageList(task_manager.Task):
return client.nova_client.images.list() return client.nova_client.images.list()
class NovaFloatingIPList(task_manager.Task):
def main(self, client):
return client.nova_client.floating_ips.list()
class NovaFloatingIPCreate(task_manager.Task):
def main(self, client):
return client.nova_client.floating_ips.create(**self.args)
class NovaFloatingIPDelete(task_manager.Task):
def main(self, client):
return client.nova_client.floating_ips.delete(**self.args)
class NovaFloatingIPAttach(task_manager.Task):
def main(self, client):
return client.nova_client.servers.add_floating_ip(**self.args)
class NovaFloatingIPDetach(task_manager.Task):
def main(self, client):
return client.nova_client.servers.remove_floating_ip(**self.args)
class MachineCreate(task_manager.Task): class MachineCreate(task_manager.Task):
def main(self, client): def main(self, client):
return client.ironic_client.node.create(**self.args) return client.ironic_client.node.create(**self.args)

View File

@ -2012,8 +2012,7 @@ class OpenStackCloud(
return self._network_client.get('/floatingips.json', params=filters) return self._network_client.get('/floatingips.json', params=filters)
def _nova_list_floating_ips(self): def _nova_list_floating_ips(self):
with _utils.shade_exceptions("Error fetching floating IPs list"): return self._compute_client.get('/os-floating-ips')
return self.manager.submit_task(_tasks.NovaFloatingIPList())
def use_external_network(self): def use_external_network(self):
return self._use_external_network return self._use_external_network
@ -4553,8 +4552,11 @@ class OpenStackCloud(
"unable to find a floating ip pool") "unable to find a floating ip pool")
pool = pools[0]['name'] pool = pools[0]['name']
pool_ip = self.manager.submit_task( pool_ip = self._compute_client.post(
_tasks.NovaFloatingIPCreate(pool=pool)) '/os-floating-ips', json=dict(pool=pool))
# TODO(mordred) Remove this - it's just for compat
pool_ip = self._compute_client.get('/os-floating-ips/{id}'.format(
id=pool_ip['id']))
return pool_ip return pool_ip
def delete_floating_ip(self, floating_ip_id, retry=1): def delete_floating_ip(self, floating_ip_id, retry=1):
@ -4622,16 +4624,12 @@ class OpenStackCloud(
def _nova_delete_floating_ip(self, floating_ip_id): def _nova_delete_floating_ip(self, floating_ip_id):
try: try:
self.manager.submit_task( self._compute_client.delete(
_tasks.NovaFloatingIPDelete(floating_ip=floating_ip_id)) '/os-floating-ips/{id}'.format(id=floating_ip_id),
except nova_exceptions.NotFound: error_message='Unable to delete floating IP {fip_id}'.format(
fip_id=floating_ip_id))
except OpenStackCloudURINotFound:
return False return False
except OpenStackCloudException:
raise
except Exception as e:
raise OpenStackCloudException(
"Unable to delete floating IP ID {fip_id}: {msg}".format(
fip_id=floating_ip_id, msg=str(e)))
return True return True
def delete_unattached_floating_ips(self, retry=1): def delete_unattached_floating_ips(self, retry=1):
@ -4853,13 +4851,22 @@ class OpenStackCloud(
def _nova_attach_ip_to_server(self, server_id, floating_ip_id, def _nova_attach_ip_to_server(self, server_id, floating_ip_id,
fixed_address=None): fixed_address=None):
with _utils.shade_exceptions( f_ip = self.get_floating_ip(
"Error attaching IP {ip} to instance {id}".format( id=floating_ip_id)
ip=floating_ip_id, id=server_id)): if f_ip is None:
f_ip = self.get_floating_ip(id=floating_ip_id) raise OpenStackCloudException(
return self.manager.submit_task(_tasks.NovaFloatingIPAttach( "unable to find floating IP {0}".format(floating_ip_id))
server=server_id, address=f_ip['floating_ip_address'], error_message = "Error attaching IP {ip} to instance {id}".format(
fixed_address=fixed_address)) ip=floating_ip_id, id=server_id)
body = {
'address': f_ip['floating_ip_address']
}
if fixed_address:
body['fixed_address'] = fixed_address
return self._compute_client.post(
'/servers/{server_id}/action'.format(server_id=server_id),
json=dict(addFloatingIp=body),
error_message=error_message)
def detach_ip_from_server(self, server_id, floating_ip_id): def detach_ip_from_server(self, server_id, floating_ip_id):
"""Detach a floating IP from a server. """Detach a floating IP from a server.
@ -4900,24 +4907,18 @@ class OpenStackCloud(
return True return True
def _nova_detach_ip_from_server(self, server_id, floating_ip_id): def _nova_detach_ip_from_server(self, server_id, floating_ip_id):
try:
f_ip = self.get_floating_ip(id=floating_ip_id) f_ip = self.get_floating_ip(id=floating_ip_id)
if f_ip is None: if f_ip is None:
raise OpenStackCloudException(
"unable to find floating IP {0}".format(floating_ip_id))
self.manager.submit_task(_tasks.NovaFloatingIPDetach(
server=server_id, address=f_ip['floating_ip_address']))
except nova_exceptions.Conflict as e:
self.log.debug(
"nova floating IP detach failed: %(msg)s", {'msg': str(e)},
exc_info=True)
return False
except OpenStackCloudException:
raise
except Exception as e:
raise OpenStackCloudException( raise OpenStackCloudException(
"Error detaching IP {ip} from instance {id}: {msg}".format( "unable to find floating IP {0}".format(floating_ip_id))
ip=floating_ip_id, id=server_id, msg=str(e))) error_message = "Error detaching IP {ip} from instance {id}".format(
ip=floating_ip_id, id=server_id)
return self._compute_client.post(
'/servers/{server_id}/action'.format(server_id=server_id),
json=dict(removeFloatingIp=dict(
address=f_ip['floating_ip_address'])),
error_message=error_message)
return True return True

View File

@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash -x
# Licensed under the Apache License, Version 2.0 (the "License"); you may # 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 # not use this file except in compliance with the License. You may obtain
@ -46,6 +46,7 @@ set +e
sudo -E -H -u jenkins tox -e$tox_env sudo -E -H -u jenkins tox -e$tox_env
EXIT_CODE=$? EXIT_CODE=$?
sudo testr last --subunit > $WORKSPACE/tempest.subunit sudo testr last --subunit > $WORKSPACE/tempest.subunit
.tox/$tox_env/bin/pbr freeze
set -e set -e
exit $EXIT_CODE exit $EXIT_CODE

View File

@ -166,8 +166,8 @@ class TestFloatingIP(base.BaseFunctionalTestCase):
content.text_content(pprint.pformat( content.text_content(pprint.pformat(
self.user_cloud.list_networks()))) self.user_cloud.list_networks())))
else: else:
# ToDo: remove once we have list/get methods for nova networks # Find network names for nova-net
nets = self.user_cloud.nova_client.networks.list() nets = self.user_cloud._compute_client.get('/os-tenant-networks')
self.addDetail( self.addDetail(
'networks-nova', 'networks-nova',
content.text_content(pprint.pformat( content.text_content(pprint.pformat(

View File

@ -19,22 +19,20 @@ test_floating_ip_nova
Tests Floating IP resource methods for nova-network Tests Floating IP resource methods for nova-network
""" """
from mock import patch
from novaclient import exceptions as n_exc
from shade import meta from shade import meta
from shade import OpenStackCloud
from shade.tests import fakes from shade.tests import fakes
from shade.tests.unit import base from shade.tests.unit import base
def has_service_side_effect(s): def get_fake_has_service(has_service):
if s == 'network': def fake_has_service(s):
return False if s == 'network':
return True return False
return has_service(s)
return fake_has_service
class TestFloatingIP(base.TestCase): class TestFloatingIP(base.RequestsMockTestCase):
mock_floating_ip_list_rep = [ mock_floating_ip_list_rep = [
{ {
'fixed_ip': None, 'fixed_ip': None,
@ -69,9 +67,6 @@ class TestFloatingIP(base.TestCase):
def setUp(self): def setUp(self):
super(TestFloatingIP, self).setUp() super(TestFloatingIP, self).setUp()
self.floating_ips = [
fakes.FakeFloatingIP(**ip) for ip in self.mock_floating_ip_list_rep
]
self.fake_server = meta.obj_to_dict( self.fake_server = meta.obj_to_dict(
fakes.FakeServer( fakes.FakeServer(
@ -83,172 +78,223 @@ class TestFloatingIP(base.TestCase):
u'OS-EXT-IPS-MAC:mac_addr': u'OS-EXT-IPS-MAC:mac_addr':
u'fa:16:3e:ae:7d:42'}]})) u'fa:16:3e:ae:7d:42'}]}))
self.floating_ip = self.cloud._normalize_floating_ips( self.cloud.has_service = get_fake_has_service(self.cloud.has_service)
meta.obj_list_to_dict(self.floating_ips))[0]
@patch.object(OpenStackCloud, 'nova_client') def test_list_floating_ips(self):
@patch.object(OpenStackCloud, 'has_service')
def test_list_floating_ips(self, mock_has_service, mock_nova_client):
mock_has_service.side_effect = has_service_side_effect
mock_nova_client.floating_ips.list.return_value = self.floating_ips
self.register_uris([
dict(method='GET',
uri=self.get_mock_url('compute', append=['os-floating-ips']),
json={'floating_ips': self.mock_floating_ip_list_rep}),
])
floating_ips = self.cloud.list_floating_ips() floating_ips = self.cloud.list_floating_ips()
mock_nova_client.floating_ips.list.assert_called_with()
self.assertIsInstance(floating_ips, list) self.assertIsInstance(floating_ips, list)
self.assertEqual(3, len(floating_ips)) self.assertEqual(3, len(floating_ips))
self.assertAreInstances(floating_ips, dict) self.assertAreInstances(floating_ips, dict)
@patch.object(OpenStackCloud, 'nova_client') self.assert_calls()
@patch.object(OpenStackCloud, 'has_service')
def test_list_floating_ips_with_filters(self, mock_has_service,
mock_nova_client):
mock_has_service.side_effect = has_service_side_effect
def test_list_floating_ips_with_filters(self):
self.assertRaisesRegex( self.assertRaisesRegex(
ValueError, "Nova-network don't support server-side", ValueError, "Nova-network don't support server-side",
self.cloud.list_floating_ips, filters={'Foo': 42} self.cloud.list_floating_ips, filters={'Foo': 42}
) )
@patch.object(OpenStackCloud, 'nova_client') def test_search_floating_ips(self):
@patch.object(OpenStackCloud, 'has_service') self.register_uris([
def test_search_floating_ips(self, mock_has_service, mock_nova_client): dict(method='GET',
mock_has_service.side_effect = has_service_side_effect uri=self.get_mock_url('compute', append=['os-floating-ips']),
mock_nova_client.floating_ips.list.return_value = self.floating_ips json={'floating_ips': self.mock_floating_ip_list_rep}),
])
floating_ips = self.cloud.search_floating_ips( floating_ips = self.cloud.search_floating_ips(
filters={'attached': False}) filters={'attached': False})
mock_nova_client.floating_ips.list.assert_called_with()
self.assertIsInstance(floating_ips, list) self.assertIsInstance(floating_ips, list)
self.assertEqual(2, len(floating_ips)) self.assertEqual(2, len(floating_ips))
self.assertAreInstances(floating_ips, dict) self.assertAreInstances(floating_ips, dict)
@patch.object(OpenStackCloud, 'nova_client') self.assert_calls()
@patch.object(OpenStackCloud, 'has_service')
def test_get_floating_ip(self, mock_has_service, mock_nova_client): def test_get_floating_ip(self):
mock_has_service.side_effect = has_service_side_effect self.register_uris([
mock_nova_client.floating_ips.list.return_value = self.floating_ips dict(method='GET',
uri=self.get_mock_url('compute', append=['os-floating-ips']),
json={'floating_ips': self.mock_floating_ip_list_rep}),
])
floating_ip = self.cloud.get_floating_ip(id='29') floating_ip = self.cloud.get_floating_ip(id='29')
mock_nova_client.floating_ips.list.assert_called_with()
self.assertIsInstance(floating_ip, dict) self.assertIsInstance(floating_ip, dict)
self.assertEqual('198.51.100.29', floating_ip['floating_ip_address']) self.assertEqual('198.51.100.29', floating_ip['floating_ip_address'])
@patch.object(OpenStackCloud, 'nova_client') self.assert_calls()
@patch.object(OpenStackCloud, 'has_service')
def test_get_floating_ip_not_found( def test_get_floating_ip_not_found(self):
self, mock_has_service, mock_nova_client): self.register_uris([
mock_has_service.side_effect = has_service_side_effect dict(method='GET',
mock_nova_client.floating_ips.list.return_value = self.floating_ips uri=self.get_mock_url('compute', append=['os-floating-ips']),
json={'floating_ips': self.mock_floating_ip_list_rep}),
])
floating_ip = self.cloud.get_floating_ip(id='666') floating_ip = self.cloud.get_floating_ip(id='666')
self.assertIsNone(floating_ip) self.assertIsNone(floating_ip)
@patch.object(OpenStackCloud, 'nova_client') self.assert_calls()
@patch.object(OpenStackCloud, 'has_service')
def test_create_floating_ip(self, mock_has_service, mock_nova_client): def test_create_floating_ip(self):
mock_has_service.side_effect = has_service_side_effect self.register_uris([
mock_nova_client.floating_ips.create.return_value =\ dict(method='POST',
fakes.FakeFloatingIP(**self.mock_floating_ip_list_rep[1]) uri=self.get_mock_url('compute', append=['os-floating-ips']),
json={'floating_ip': self.mock_floating_ip_list_rep[1]},
validate=dict(
json={'pool': 'nova'})),
dict(method='GET',
uri=self.get_mock_url(
'compute',
append=['os-floating-ips', '2']),
json={'floating_ips': self.mock_floating_ip_list_rep[1]}),
])
self.cloud.create_floating_ip(network='nova') self.cloud.create_floating_ip(network='nova')
mock_nova_client.floating_ips.create.assert_called_with(pool='nova') self.assert_calls()
@patch.object(OpenStackCloud, '_nova_list_floating_ips') def test_available_floating_ip_existing(self):
@patch.object(OpenStackCloud, 'has_service') self.register_uris([
def test_available_floating_ip_existing( dict(method='GET',
self, mock_has_service, mock__nova_list_floating_ips): uri=self.get_mock_url('compute', append=['os-floating-ips']),
mock_has_service.side_effect = has_service_side_effect json={'floating_ips': self.mock_floating_ip_list_rep[:1]}),
mock__nova_list_floating_ips.return_value = \ ])
self.mock_floating_ip_list_rep[:1]
ip = self.cloud.available_floating_ip(network='nova') ip = self.cloud.available_floating_ip(network='nova')
self.assertEqual(self.mock_floating_ip_list_rep[0]['ip'], self.assertEqual(self.mock_floating_ip_list_rep[0]['ip'],
ip['floating_ip_address']) ip['floating_ip_address'])
self.assert_calls()
@patch.object(OpenStackCloud, 'nova_client') def test_available_floating_ip_new(self):
@patch.object(OpenStackCloud, '_nova_list_floating_ips') self.register_uris([
@patch.object(OpenStackCloud, 'has_service') dict(method='GET',
def test_available_floating_ip_new( uri=self.get_mock_url('compute', append=['os-floating-ips']),
self, mock_has_service, mock__nova_list_floating_ips, json={'floating_ips': []}),
mock_nova_client): dict(method='POST',
mock_has_service.side_effect = has_service_side_effect uri=self.get_mock_url('compute', append=['os-floating-ips']),
mock__nova_list_floating_ips.return_value = [] json={'floating_ip': self.mock_floating_ip_list_rep[0]},
mock_nova_client.floating_ips.create.return_value = \ validate=dict(
fakes.FakeFloatingIP(**self.mock_floating_ip_list_rep[0]) json={'pool': 'nova'})),
dict(method='GET',
uri=self.get_mock_url(
'compute',
append=['os-floating-ips', '1']),
json={'floating_ip': self.mock_floating_ip_list_rep[0]}),
])
ip = self.cloud.available_floating_ip(network='nova') ip = self.cloud.available_floating_ip(network='nova')
self.assertEqual(self.mock_floating_ip_list_rep[0]['ip'], self.assertEqual(self.mock_floating_ip_list_rep[0]['ip'],
ip['floating_ip_address']) ip['floating_ip_address'])
self.assert_calls()
@patch.object(OpenStackCloud, 'nova_client') def test_delete_floating_ip_existing(self):
@patch.object(OpenStackCloud, 'has_service')
def test_delete_floating_ip_existing( self.register_uris([
self, mock_has_service, mock_nova_client): dict(method='DELETE',
mock_has_service.side_effect = has_service_side_effect uri=self.get_mock_url(
mock_nova_client.floating_ips.delete.return_value = None 'compute',
append=['os-floating-ips', 'a-wild-id-appears'])),
dict(method='GET',
uri=self.get_mock_url('compute', append=['os-floating-ips']),
json={'floating_ips': []}),
])
ret = self.cloud.delete_floating_ip( ret = self.cloud.delete_floating_ip(
floating_ip_id='a-wild-id-appears') floating_ip_id='a-wild-id-appears')
mock_nova_client.floating_ips.delete.assert_called_with(
floating_ip='a-wild-id-appears')
self.assertTrue(ret) self.assertTrue(ret)
self.assert_calls()
@patch.object(OpenStackCloud, 'nova_client') def test_delete_floating_ip_not_found(self):
@patch.object(OpenStackCloud, 'get_floating_ip') self.register_uris([
@patch.object(OpenStackCloud, '_use_neutron_floating') dict(method='DELETE',
def test_delete_floating_ip_not_found( uri=self.get_mock_url(
self, mock_use_floating, mock_get_floating_ip, mock_nova_client): 'compute',
mock_use_floating.return_value = False append=['os-floating-ips', 'a-wild-id-appears']),
mock_get_floating_ip.return_value = None status_code=404),
mock_nova_client.floating_ips.delete.side_effect = n_exc.NotFound( ])
code=404)
ret = self.cloud.delete_floating_ip( ret = self.cloud.delete_floating_ip(
floating_ip_id='a-wild-id-appears') floating_ip_id='a-wild-id-appears')
self.assertFalse(ret) self.assertFalse(ret)
self.assert_calls()
@patch.object(OpenStackCloud, 'nova_client') def test_attach_ip_to_server(self):
@patch.object(OpenStackCloud, 'has_service') self.register_uris([
def test_attach_ip_to_server(self, mock_has_service, mock_nova_client): dict(method='GET',
mock_has_service.side_effect = has_service_side_effect uri=self.get_mock_url('compute', append=['os-floating-ips']),
mock_nova_client.floating_ips.list.return_value = self.floating_ips json={'floating_ips': self.mock_floating_ip_list_rep}),
dict(method='POST',
uri=self.get_mock_url(
'compute',
append=['servers', self.fake_server.id, 'action']),
validate=dict(
json={
"addFloatingIp": {
"address": "203.0.113.1",
"fixed_address": "192.0.2.129",
}})),
])
self.cloud._attach_ip_to_server( self.cloud._attach_ip_to_server(
server=self.fake_server, floating_ip=self.floating_ip, server=self.fake_server,
floating_ip=self.cloud._normalize_floating_ip(
self.mock_floating_ip_list_rep[0]),
fixed_address='192.0.2.129') fixed_address='192.0.2.129')
mock_nova_client.servers.add_floating_ip.assert_called_with( self.assert_calls()
server='server-id', address='203.0.113.1',
fixed_address='192.0.2.129')
@patch.object(OpenStackCloud, 'nova_client') def test_detach_ip_from_server(self):
@patch.object(OpenStackCloud, 'has_service') self.register_uris([
def test_detach_ip_from_server(self, mock_has_service, mock_nova_client): dict(method='GET',
mock_has_service.side_effect = has_service_side_effect uri=self.get_mock_url('compute', append=['os-floating-ips']),
mock_nova_client.floating_ips.list.return_value = [ json={'floating_ips': self.mock_floating_ip_list_rep}),
fakes.FakeFloatingIP(**ip) for ip in self.mock_floating_ip_list_rep dict(method='POST',
] uri=self.get_mock_url(
'compute',
append=['servers', self.fake_server.id, 'action']),
validate=dict(
json={
"removeFloatingIp": {
"address": "203.0.113.1",
}})),
])
self.cloud.detach_ip_from_server( self.cloud.detach_ip_from_server(
server_id='server-id', floating_ip_id=1) server_id='server-id', floating_ip_id=1)
self.assert_calls()
mock_nova_client.servers.remove_floating_ip.assert_called_with( def test_add_ip_from_pool(self):
server='server-id', address='203.0.113.1') self.register_uris([
dict(method='GET',
@patch.object(OpenStackCloud, 'nova_client') uri=self.get_mock_url('compute', append=['os-floating-ips']),
@patch.object(OpenStackCloud, 'has_service') json={'floating_ips': self.mock_floating_ip_list_rep}),
def test_add_ip_from_pool(self, mock_has_service, mock_nova_client): dict(method='GET',
mock_has_service.side_effect = has_service_side_effect uri=self.get_mock_url('compute', append=['os-floating-ips']),
mock_nova_client.floating_ips.list.return_value = self.floating_ips json={'floating_ips': self.mock_floating_ip_list_rep}),
dict(method='POST',
uri=self.get_mock_url(
'compute',
append=['servers', self.fake_server.id, 'action']),
validate=dict(
json={
"addFloatingIp": {
"address": "203.0.113.1",
"fixed_address": "192.0.2.129",
}})),
])
server = self.cloud._add_ip_from_pool( server = self.cloud._add_ip_from_pool(
server=self.fake_server, server=self.fake_server,
@ -256,16 +302,8 @@ class TestFloatingIP(base.TestCase):
fixed_address='192.0.2.129') fixed_address='192.0.2.129')
self.assertEqual(server, self.fake_server) self.assertEqual(server, self.fake_server)
self.assert_calls()
@patch.object(OpenStackCloud, 'delete_floating_ip') def test_cleanup_floating_ips(self):
@patch.object(OpenStackCloud, 'list_floating_ips') # This should not call anything because it's unsafe on nova.
@patch.object(OpenStackCloud, '_use_neutron_floating')
def test_cleanup_floating_ips(
self, mock_use_neutron_floating, mock_list_floating_ips,
mock_delete_floating_ip):
mock_use_neutron_floating.return_value = False
self.cloud.delete_unattached_floating_ips() self.cloud.delete_unattached_floating_ips()
mock_delete_floating_ip.assert_not_called()
mock_list_floating_ips.assert_not_called()

View File

@ -26,7 +26,7 @@ setenv =
passenv = OS_* SHADE_* passenv = OS_* SHADE_*
whitelist_externals = bash whitelist_externals = bash
commands = commands =
bash extras/install-tips.sh bash -x {toxinidir}/extras/install-tips.sh
python setup.py testr --slowest --testr-args='--concurrency=1 {posargs}' python setup.py testr --slowest --testr-args='--concurrency=1 {posargs}'
[testenv:pep8] [testenv:pep8]