fix issue with Nova passthrough deprecation

Nova Networking was deprecated in Newton and some of the API calls
that were part of NN were still in use by Trove. With the arrival of
python-novaclient 8.0.0, this caused a failure.

Nova also deprecated the image pass through capability. The management
API uses some of that to test whether an image id is valid or
not. This change fixes the management API, and the attendant scenario
tests.

Depends-On: I142f97d691fa55e9824714c9c224f998ad72337e

Change-Id: I2f2a12207581a94fb8561a6d65a3a79b4a29b063
Closes-Bug: #1690936
This commit is contained in:
Amrith Kumar 2017-05-23 22:22:26 -04:00 committed by Sean Dague
parent 012da9a334
commit 3ed3ea94bb
10 changed files with 86 additions and 56 deletions

View File

@ -13,6 +13,10 @@
"volume_service_type":"volume"
},
"glance_client": {
"auth_url":"http://%service_host%/identity/v2.0"
},
"flavors": null,
"white_box":false,
@ -74,7 +78,7 @@
"tenant_id":"%demo_tenant_id%",
"requirements": {
"is_admin":false,
"services": ["nova", "trove"]
"services": ["nova", "glance", "trove"]
}
}
],

View File

@ -66,6 +66,8 @@ common_opts = [
help='Service endpoint type to use when searching catalog.'),
cfg.StrOpt('nova_client_version', default='2.12',
help="The version of the compute service client."),
cfg.StrOpt('glance_client_version', default='2',
help="The version of the image service client."),
cfg.URIOpt('neutron_url', help='URL without the tenant segment.'),
cfg.StrOpt('neutron_service_type', default='network',
help='Service type to use when searching catalog.'),

View File

@ -47,7 +47,8 @@ def glance_client(context, region_name=None):
auth = v3.Token(CONF.trove_auth_url, context.auth_token)
session = ka_session.Session(auth=auth)
return Client('2', endpoint=endpoint_url, session=session)
return Client(CONF.glance_client_version, endpoint=endpoint_url,
session=session)
create_glance_client = import_class(CONF.remote_glance_client)

View File

@ -13,14 +13,14 @@
# limitations under the License.
from novaclient import exceptions as nova_exceptions
from glanceclient import exc as glance_exceptions
from oslo_log import log as logging
from trove.common import apischema as apischema
from trove.common.auth import admin_context
from trove.common import exception
from trove.common import glance_remote
from trove.common.i18n import _
from trove.common import remote
from trove.common import utils
from trove.common import wsgi
from trove.datastore import models
@ -53,10 +53,10 @@ class DatastoreVersionController(wsgi.Controller):
{'tenant': tenant_id, 'version': version_name,
'datastore': datastore_name})
client = remote.create_nova_client(context)
client = glance_remote.create_glance_client(context)
try:
client.images.get(image_id)
except nova_exceptions.NotFound:
except glance_exceptions.HTTPNotFound:
raise exception.ImageNotFound(uuid=image_id)
try:
@ -119,10 +119,10 @@ class DatastoreVersionController(wsgi.Controller):
if type(packages) is list:
packages = ','.join(packages)
client = remote.create_nova_client(context)
client = glance_remote.create_glance_client(context)
try:
client.images.get(image_id)
except nova_exceptions.NotFound:
except glance_exceptions.HTTPNotFound:
raise exception.ImageNotFound(uuid=image_id)
models.update_datastore_version(datastore_version.datastore_name,

View File

@ -1345,8 +1345,11 @@ class BuiltInstanceTasks(BuiltInstance, NotifyMixin, ConfigurationMixin):
def _get_floating_ips(self):
"""Returns floating ips as a dict indexed by the ip."""
floating_ips = {}
for ip in self.nova_client.floating_ips.list():
floating_ips.update({ip.ip: ip})
neutron_client = remote.create_neutron_client(self.context)
network_floating_ips = neutron_client.list_floatingips()
for ip in network_floating_ips.get('floatingips'):
floating_ips.update({ip.get('floating_ip_address'): ip})
LOG.debug("In _get_floating_ips(), returning %s" % floating_ips)
return floating_ips
def detach_public_ips(self):

View File

@ -25,7 +25,7 @@ from troveclient.compat import exceptions
from trove.tests.config import CONFIG
from trove.tests.util import create_client
from trove.tests.util import create_dbaas_client
from trove.tests.util import create_nova_client
from trove.tests.util import create_glance_client
from trove.tests.util import test_config
from trove.tests.util.users import Requirements
@ -49,11 +49,14 @@ class MgmtDataStoreVersion(object):
reqs = Requirements(is_admin=True)
self.user = CONFIG.users.find_user(reqs)
self.client = create_dbaas_client(self.user)
if test_config.nova_client is not None:
nova_user = test_config.users.find_user(
Requirements(services=["nova"]))
self.nova_client = create_nova_client(nova_user)
self.images = self.nova_client.images.list()
self.images = []
if test_config.glance_client is not None:
glance_user = test_config.users.find_user(
Requirements(services=["glance"]))
self.glance_client = create_glance_client(glance_user)
images = self.glance_client.images.list()
for image in images:
self.images.append(image.id)
def _find_ds_version_by_name(self, ds_version_name):
ds_versions = self.client.mgmt_datastore_versions.list()
@ -112,7 +115,7 @@ class MgmtDataStoreVersion(object):
"""Tests the mgmt datastore version create method."""
response = self.client.mgmt_datastore_versions.create(
'test_version1', 'test_ds', 'test_mgr',
self.images[0].id, ['vertica-7.1'])
self.images[0], ['vertica-7.1'])
assert_equal(None, response)
assert_equal(202, self.client.last_http_code)
@ -127,7 +130,7 @@ class MgmtDataStoreVersion(object):
assert_equal('test_version1', self.created_version.name)
assert_equal('test_ds', self.created_version.datastore_name)
assert_equal('test_mgr', self.created_version.datastore_manager)
assert_equal(self.images[0].id, self.created_version.image)
assert_equal(self.images[0], self.created_version.image)
assert_equal(['vertica-7.1'], self.created_version.packages)
assert_true(self.created_version.active)
assert_false(self.created_version.default)
@ -136,13 +139,13 @@ class MgmtDataStoreVersion(object):
def test_mgmt_ds_version_patch(self):
"""Tests the mgmt datastore version edit method."""
self.client.mgmt_datastore_versions.edit(
self.created_version.id, image=self.images[1].id,
self.created_version.id, image=self.images[1],
packages=['pkg1'])
assert_equal(202, self.client.last_http_code)
# Lets match the content of patched datastore
patched_ds_version = self._find_ds_version_by_name('test_version1')
assert_equal(self.images[1].id, patched_ds_version.image)
assert_equal(self.images[1], patched_ds_version.image)
assert_equal(['pkg1'], patched_ds_version.packages)
@test(depends_on=[test_mgmt_ds_version_patch])

View File

@ -18,7 +18,7 @@ from mock import Mock, patch, MagicMock, PropertyMock
from testtools.matchers import Is, Equals
from trove.common import exception
from trove.common import remote
from trove.common import glance_remote
from trove.datastore import models as datastore_models
from trove.extensions.mgmt.datastores.service import DatastoreVersionController
from trove.tests.unittests import trove_testtools
@ -81,14 +81,14 @@ class TestDatastoreVersionController(trove_testtools.TestCase):
self.assertIn("'' is too short", error_messages)
self.assertIn("'' does not match '^.*[0-9a-zA-Z]+.*$'", error_messages)
@patch.object(remote, 'create_nova_client')
@patch.object(glance_remote, 'create_glance_client')
@patch.object(datastore_models.Datastore, 'load')
@patch.object(datastore_models.DatastoreVersion, 'load',
side_effect=exception.DatastoreVersionNotFound)
@patch.object(datastore_models, 'update_datastore_version')
def test_create_datastore_versions(self, mock_ds_version_create,
mock_ds_version_load,
mock_ds_load, mock_nova_client):
mock_ds_load, mock_glance_client):
body = self.version
mock_ds_load.return_value.name = 'test_dsx'
@ -127,12 +127,12 @@ class TestDatastoreVersionController(trove_testtools.TestCase):
mock_ds_version_load_all.assert_called_with(only_active=False)
mock_ds_version_load_by_uuid.assert_called_with(mock_id)
@patch.object(remote, 'create_nova_client')
@patch.object(glance_remote, 'create_glance_client')
@patch.object(datastore_models.DatastoreVersion, 'load_by_uuid')
@patch.object(datastore_models, 'update_datastore_version')
def test_edit_datastore_versions(self, mock_ds_version_update,
mock_ds_version_load,
mock_nova_client):
mock_glance_client):
body = {'image': '21c8805a-a800-4bca-a192-3a5a2519044d'}
mock_ds_version = MagicMock()

View File

@ -13,10 +13,10 @@
# limitations under the License.
from mock import Mock, patch
from novaclient import exceptions as nova_exceptions
from glanceclient import exc as glance_exceptions
from trove.common import exception
from trove.common import remote
from trove.common import glance_remote
from trove.datastore import models
from trove.extensions.mgmt.datastores.service import DatastoreVersionController
from trove.tests.unittests import trove_testtools
@ -48,8 +48,8 @@ class TestDatastoreVersion(trove_testtools.TestCase):
def tearDown(self):
super(TestDatastoreVersion, self).tearDown()
@patch.object(remote, 'create_nova_client')
def test_version_create(self, mock_nova_client):
@patch.object(glance_remote, 'create_glance_client')
def test_version_create(self, mock_glance_client):
body = {"version": {
"datastore_name": "test_ds",
"name": "test_vr",
@ -62,10 +62,10 @@ class TestDatastoreVersion(trove_testtools.TestCase):
self.req, body, self.tenant_id)
self.assertEqual(202, output.status)
@patch.object(remote, 'create_nova_client')
@patch.object(glance_remote, 'create_glance_client')
@patch.object(models.DatastoreVersion, 'load')
def test_fail_already_exists_version_create(self, mock_load,
mock_nova_client):
mock_glance_client):
body = {"version": {
"datastore_name": "test_ds",
"name": "test_new_vr",
@ -79,12 +79,10 @@ class TestDatastoreVersion(trove_testtools.TestCase):
"A datastore version with the name 'test_new_vr' already exists",
self.version_controller.create, self.req, body, self.tenant_id)
@patch.object(remote, 'create_nova_client')
def test_fail_image_not_found_version_create(self, mock_nova_client):
mock_nova_client.return_value.images.get = Mock(
side_effect=nova_exceptions.NotFound(404,
"Image id not found image-id"
))
@patch.object(glance_remote, 'create_glance_client')
def test_fail_image_not_found_version_create(self, mock_glance_client):
mock_glance_client.return_value.images.get = Mock(
side_effect=glance_exceptions.HTTPNotFound())
body = {"version": {
"datastore_name": "test_ds",
"name": "test_vr",
@ -114,7 +112,7 @@ class TestDatastoreVersion(trove_testtools.TestCase):
exception.DatastoreVersionNotFound,
err_msg, models.DatastoreVersion.load_by_uuid, ds_version1.id)
@patch.object(remote, 'create_nova_client')
@patch.object(glance_remote, 'create_glance_client')
def test_version_update(self, mock_client):
body = {"image": "c022f4dc-76ed-4e3f-a25e-33e031f43f8b"}
output = self.version_controller.edit(self.req, body,
@ -127,12 +125,10 @@ class TestDatastoreVersion(trove_testtools.TestCase):
self.ds_version2.id)
self.assertEqual(body['image'], test_ds_version.image_id)
@patch.object(remote, 'create_nova_client')
def test_version_update_fail_image_not_found(self, mock_nova_client):
mock_nova_client.return_value.images.get = Mock(
side_effect=nova_exceptions.NotFound(404,
"Image id not found image-id"
))
@patch.object(glance_remote, 'create_glance_client')
def test_version_update_fail_image_not_found(self, mock_glance_client):
mock_glance_client.return_value.images.get = Mock(
side_effect=glance_exceptions.HTTPNotFound())
body = {"image": "non-existent-image-id"}
self.assertRaisesRegexp(

View File

@ -59,6 +59,11 @@ INST_ID = 'dbinst-id-1'
VOLUME_ID = 'volume-id-1'
class _fake_neutron_client(object):
def list_floatingips(self):
return {'floatingips': [{'floating_ip_address': '192.168.10.1'}]}
class FakeOptGroup(object):
def __init__(self, tcp_ports=['3306', '3301-3307'],
udp_ports=[], icmp=False):
@ -740,14 +745,6 @@ class BuiltInstanceTasksTest(trove_testtools.TestCase):
if 'volume' in self._testMethodName:
self._stub_volume_client()
stub_floating_ips_manager = MagicMock(
spec=novaclient.v2.floating_ips.FloatingIPManager)
self.instance_task._nova_client.floating_ips = (
stub_floating_ips_manager)
floatingip = novaclient.v2.floating_ips.FloatingIP(
stub_floating_ips_manager, {'ip': '192.168.10.1'}, True)
stub_floating_ips_manager.list = MagicMock(return_value=[floatingip])
def tearDown(self):
super(BuiltInstanceTasksTest, self).tearDown()
@ -878,14 +875,20 @@ class BuiltInstanceTasksTest(trove_testtools.TestCase):
Mock())
def test_get_floating_ips(self):
floating_ips = self.instance_task._get_floating_ips()
self.assertEqual('192.168.10.1', floating_ips['192.168.10.1'].ip)
with patch.object(remote, 'create_neutron_client',
return_value=_fake_neutron_client()):
floating_ips = self.instance_task._get_floating_ips()
self.assertEqual('192.168.10.1',
floating_ips['192.168.10.1'].get(
'floating_ip_address'))
@patch.object(BaseInstance, 'get_visible_ip_addresses',
return_value=['192.168.10.1'])
def test_detach_public_ips(self, mock_address):
removed_ips = self.instance_task.detach_public_ips()
self.assertEqual(['192.168.10.1'], removed_ips)
with patch.object(remote, 'create_neutron_client',
return_value=_fake_neutron_client()):
removed_ips = self.instance_task.detach_public_ips()
self.assertEqual(['192.168.10.1'], removed_ips)
def test_attach_public_ips(self):
self.instance_task.attach_public_ips(['192.168.10.1'])

View File

@ -177,6 +177,24 @@ def create_nova_client(user, service_type=None):
return TestClient(openstack)
def create_glance_client(user):
"""Creates a rich client for the Glance API using the test config."""
if test_config.glance_client is None:
raise SkipTest("No glance_client info specified in the Test Config "
"so this test will be skipped.")
from glanceclient import Client
from keystoneauth1.identity import v2
from keystoneauth1 import session
auth = v2.Password(username=user.auth_user,
password=user.auth_key,
tenant_name=user.tenant,
auth_url=test_config.glance_client['auth_url'])
session = session.Session(auth=auth)
glance = Client(CONF.glance_client_version, session=session)
return TestClient(glance)
def dns_checker(mgmt_instance):
"""Given a MGMT instance, ensures DNS provisioning worked.