Update openstacksdk construction

The queens release of python-openstacksdk breaks the sdk and senlin code
that's in heat right now. This shifts us to the new world order.

python-senlinclient is actually just a thin wrapper around
python-openstacksdk. Instead of reworking the constructor in the Senlin
plugin, just remove use of python-senlinclient and use the openstacksdk
client plugin instead.

Change-Id: Idf0acebf7b3774db26e335b3f3229227bfe68502
This commit is contained in:
Monty Taylor 2018-01-05 19:56:46 -06:00
parent 164a8ab2ba
commit b9d7d1e640
No known key found for this signature in database
GPG Key ID: 7BAE94BC7141A594
9 changed files with 72 additions and 66 deletions

View File

@ -60,4 +60,3 @@ We have integration with
* https://git.openstack.org/cgit/openstack/python-mistralclient (workflow service) * https://git.openstack.org/cgit/openstack/python-mistralclient (workflow service)
* https://git.openstack.org/cgit/openstack/python-zaqarclient (messaging service) * https://git.openstack.org/cgit/openstack/python-zaqarclient (messaging service)
* https://git.openstack.org/cgit/openstack/python-monascaclient (monitoring service) * https://git.openstack.org/cgit/openstack/python-monascaclient (monitoring service)
* https://git.openstack.org/cgit/openstack/python-senlinclient (clustering service)

View File

@ -11,12 +11,15 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from openstack.config import cloud_region
from openstack import connection from openstack import connection
from openstack import exceptions from openstack import exceptions
from openstack import profile import os_service_types
from heat.common import config
from heat.engine.clients import client_plugin from heat.engine.clients import client_plugin
from heat.engine import constraints from heat.engine import constraints
import heat.version
CLIENT_NAME = 'openstack' CLIENT_NAME = 'openstack'
@ -25,24 +28,39 @@ class OpenStackSDKPlugin(client_plugin.ClientPlugin):
exceptions_module = exceptions exceptions_module = exceptions
service_types = [NETWORK] = ['network'] service_types = [NETWORK, CLUSTERING] = ['network', 'clustering']
service_client_map = {NETWORK: 'neutron'}
api_version_map = {NETWORK: '2.0'}
def _create(self, version=None): def _create(self, version=None):
prof = profile.Profile() config = cloud_region.from_session(
for svc_type in self.service_types: # TODO(mordred) The way from_session calculates a cloud name
interface = self._get_client_option( # doesn't interact well with the mocks in the test cases. The
self.service_client_map[svc_type], 'endpoint_type') # name is used in logging to distinguish requests made to different
prof.set_interface(svc_type, interface) # clouds. For now, set it to local - but maybe find a way to set
prof.set_region(svc_type, self._get_region_name()) # it to something more meaningful later.
prof.set_version(svc_type, self.api_version_map[svc_type]) name='local',
session=self.context.keystone_session,
config=self._get_service_interfaces(),
region_name=self._get_region_name(),
app_name='heat',
app_version=heat.version.version_info.version_string())
return connection.Connection(config=config)
key_session = self.context.keystone_session def _get_service_interfaces(self):
return connection.Connection(authenticator=key_session.auth, interfaces = {}
verify=key_session.verify, if not os_service_types:
cert=key_session.cert, return interfaces
profile=prof) types = os_service_types.ServiceTypes()
for name, _ in config.list_opts():
if not name or not name.startswith('clients_'):
continue
project_name = name.split("_", 1)[0]
service_data = types.get_service_data_for_project(project_name)
if not service_data:
continue
service_type = service_data['service_type']
interfaces[service_type + '_interface'] = self._get_client_option(
service_type, 'endpoint_type')
return interfaces
def is_not_found(self, ex): def is_not_found(self, ex):
return isinstance(ex, exceptions.ResourceNotFound) return isinstance(ex, exceptions.ResourceNotFound)

View File

@ -11,30 +11,23 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from openstack import exceptions
from heat.common import exception from heat.common import exception
from heat.common.i18n import _ from heat.common.i18n import _
from heat.engine.clients import client_plugin from heat.engine.clients.os import openstacksdk as sdk_plugin
from heat.engine import constraints from heat.engine import constraints
from openstack import profile
from senlinclient.common import exc
from senlinclient.v1 import client
CLIENT_NAME = 'senlin' CLIENT_NAME = 'senlin'
class SenlinClientPlugin(client_plugin.ClientPlugin): class SenlinClientPlugin(sdk_plugin.OpenStackSDKPlugin):
service_types = [CLUSTERING] = ['clustering'] exceptions_module = exceptions
def _create(self): def _create(self, version=None):
interface = self._get_client_option(CLIENT_NAME, 'endpoint_type') client = super(SenlinClientPlugin, self)._create(version=version)
prof = profile.Profile() return client.clustering
prof.set_interface(self.CLUSTERING, interface)
prof.set_region(self.CLUSTERING, self._get_region_name())
key_session = self.context.keystone_session
return client.Client(prof=prof,
authenticator=key_session.auth)
def generate_spec(self, spec_type, spec_props): def generate_spec(self, spec_type, spec_props):
spec = {'properties': spec_props} spec = {'properties': spec_props}
@ -64,11 +57,8 @@ class SenlinClientPlugin(client_plugin.ClientPlugin):
policy = self.client().get_policy(policy_name) policy = self.client().get_policy(policy_name)
return policy.id return policy.id
def is_not_found(self, ex):
return isinstance(ex, exc.sdkexc.ResourceNotFound)
def is_bad_request(self, ex): def is_bad_request(self, ex):
return (isinstance(ex, exc.sdkexc.HttpException) and return (isinstance(ex, exceptions.HttpException) and
ex.status_code == 400) ex.status_code == 400)
def execute_actions(self, actions): def execute_actions(self, actions):
@ -93,24 +83,24 @@ class SenlinClientPlugin(client_plugin.ClientPlugin):
class ProfileConstraint(constraints.BaseCustomConstraint): class ProfileConstraint(constraints.BaseCustomConstraint):
# If name is not unique, will raise exc.sdkexc.HttpException # If name is not unique, will raise exceptions.HttpException
expected_exceptions = (exc.sdkexc.HttpException,) expected_exceptions = (exceptions.HttpException,)
def validate_with_client(self, client, profile): def validate_with_client(self, client, profile):
client.client(CLIENT_NAME).get_profile(profile) client.client(CLIENT_NAME).get_profile(profile)
class ClusterConstraint(constraints.BaseCustomConstraint): class ClusterConstraint(constraints.BaseCustomConstraint):
# If name is not unique, will raise exc.sdkexc.HttpException # If name is not unique, will raise exceptions.HttpException
expected_exceptions = (exc.sdkexc.HttpException,) expected_exceptions = (exceptions.HttpException,)
def validate_with_client(self, client, value): def validate_with_client(self, client, value):
client.client(CLIENT_NAME).get_cluster(value) client.client(CLIENT_NAME).get_cluster(value)
class PolicyConstraint(constraints.BaseCustomConstraint): class PolicyConstraint(constraints.BaseCustomConstraint):
# If name is not unique, will raise exc.sdkexc.HttpException # If name is not unique, will raise exceptions.HttpException
expected_exceptions = (exc.sdkexc.HttpException,) expected_exceptions = (exceptions.HttpException,)
def validate_with_client(self, client, value): def validate_with_client(self, client, value):
client.client(CLIENT_NAME).get_policy(value) client.client(CLIENT_NAME).get_policy(value)
@ -121,8 +111,8 @@ class ProfileTypeConstraint(constraints.BaseCustomConstraint):
expected_exceptions = (exception.StackValidationFailed,) expected_exceptions = (exception.StackValidationFailed,)
def validate_with_client(self, client, value): def validate_with_client(self, client, value):
senlin_client = client.client(CLIENT_NAME) conn = client.client(CLIENT_NAME)
type_list = senlin_client.profile_types() type_list = conn.profile_types()
names = [pt.name for pt in type_list] names = [pt.name for pt in type_list]
if value not in names: if value not in names:
not_found_message = ( not_found_message = (
@ -138,8 +128,8 @@ class PolicyTypeConstraint(constraints.BaseCustomConstraint):
expected_exceptions = (exception.StackValidationFailed,) expected_exceptions = (exception.StackValidationFailed,)
def validate_with_client(self, client, value): def validate_with_client(self, client, value):
senlin_client = client.client(CLIENT_NAME) conn = client.client(CLIENT_NAME)
type_list = senlin_client.policy_types() type_list = conn.policy_types()
names = [pt.name for pt in type_list] names = [pt.name for pt in type_list]
if value not in names: if value not in names:
not_found_message = ( not_found_message = (

View File

@ -12,7 +12,7 @@
# under the License. # under the License.
import mock import mock
from senlinclient.common import exc from openstack import exceptions
from heat.engine.clients.os import senlin as senlin_plugin from heat.engine.clients.os import senlin as senlin_plugin
from heat.tests import common from heat.tests import common
@ -32,10 +32,10 @@ class SenlinClientPluginTest(common.HeatTestCase):
def test_is_bad_request(self): def test_is_bad_request(self):
self.assertTrue(self.plugin.is_bad_request( self.assertTrue(self.plugin.is_bad_request(
exc.sdkexc.HttpException(http_status=400))) exceptions.HttpException(http_status=400)))
self.assertFalse(self.plugin.is_bad_request(Exception)) self.assertFalse(self.plugin.is_bad_request(Exception))
self.assertFalse(self.plugin.is_bad_request( self.assertFalse(self.plugin.is_bad_request(
exc.sdkexc.HttpException(http_status=404))) exceptions.HttpException(http_status=404)))
def test_check_action_success(self): def test_check_action_success(self):
mock_action = mock.MagicMock() mock_action = mock.MagicMock()
@ -87,10 +87,10 @@ class ProfileConstraintTest(common.HeatTestCase):
self.assertTrue(self.constraint.validate("PROFILE_ID", self.ctx)) self.assertTrue(self.constraint.validate("PROFILE_ID", self.ctx))
def test_validate_false(self): def test_validate_false(self):
self.mock_get_profile.side_effect = exc.sdkexc.ResourceNotFound( self.mock_get_profile.side_effect = exceptions.ResourceNotFound(
'PROFILE_ID') 'PROFILE_ID')
self.assertFalse(self.constraint.validate("PROFILE_ID", self.ctx)) self.assertFalse(self.constraint.validate("PROFILE_ID", self.ctx))
self.mock_get_profile.side_effect = exc.sdkexc.HttpException( self.mock_get_profile.side_effect = exceptions.HttpException(
'PROFILE_ID') 'PROFILE_ID')
self.assertFalse(self.constraint.validate("PROFILE_ID", self.ctx)) self.assertFalse(self.constraint.validate("PROFILE_ID", self.ctx))
@ -112,10 +112,10 @@ class ClusterConstraintTest(common.HeatTestCase):
self.assertTrue(self.constraint.validate("CLUSTER_ID", self.ctx)) self.assertTrue(self.constraint.validate("CLUSTER_ID", self.ctx))
def test_validate_false(self): def test_validate_false(self):
self.mock_get_cluster.side_effect = exc.sdkexc.ResourceNotFound( self.mock_get_cluster.side_effect = exceptions.ResourceNotFound(
'CLUSTER_ID') 'CLUSTER_ID')
self.assertFalse(self.constraint.validate("CLUSTER_ID", self.ctx)) self.assertFalse(self.constraint.validate("CLUSTER_ID", self.ctx))
self.mock_get_cluster.side_effect = exc.sdkexc.HttpException( self.mock_get_cluster.side_effect = exceptions.HttpException(
'CLUSTER_ID') 'CLUSTER_ID')
self.assertFalse(self.constraint.validate("CLUSTER_ID", self.ctx)) self.assertFalse(self.constraint.validate("CLUSTER_ID", self.ctx))
@ -137,10 +137,10 @@ class PolicyConstraintTest(common.HeatTestCase):
self.assertTrue(self.constraint.validate("POLICY_ID", self.ctx)) self.assertTrue(self.constraint.validate("POLICY_ID", self.ctx))
def test_validate_false(self): def test_validate_false(self):
self.mock_get_policy.side_effect = exc.sdkexc.ResourceNotFound( self.mock_get_policy.side_effect = exceptions.ResourceNotFound(
'POLICY_ID') 'POLICY_ID')
self.assertFalse(self.constraint.validate("POLICY_ID", self.ctx)) self.assertFalse(self.constraint.validate("POLICY_ID", self.ctx))
self.mock_get_policy.side_effect = exc.sdkexc.HttpException( self.mock_get_policy.side_effect = exceptions.HttpException(
'POLICY_ID') 'POLICY_ID')
self.assertFalse(self.constraint.validate("POLICY_ID", self.ctx)) self.assertFalse(self.constraint.validate("POLICY_ID", self.ctx))

View File

@ -26,7 +26,7 @@ from heat.engine import scheduler
from heat.engine import template from heat.engine import template
from heat.tests import common from heat.tests import common
from heat.tests import utils from heat.tests import utils
from senlinclient.common import exc from openstack import exceptions
cluster_stack_template = """ cluster_stack_template = """
@ -165,7 +165,7 @@ class SenlinClusterTest(common.HeatTestCase):
def test_cluster_delete_success(self): def test_cluster_delete_success(self):
cluster = self._create_cluster(self.t) cluster = self._create_cluster(self.t)
self.senlin_mock.get_cluster.side_effect = [ self.senlin_mock.get_cluster.side_effect = [
exc.sdkexc.ResourceNotFound('SenlinCluster'), exceptions.ResourceNotFound('SenlinCluster'),
] ]
scheduler.TaskRunner(cluster.delete)() scheduler.TaskRunner(cluster.delete)()
self.senlin_mock.delete_cluster.assert_called_once_with( self.senlin_mock.delete_cluster.assert_called_once_with(

View File

@ -25,7 +25,7 @@ from heat.engine import scheduler
from heat.engine import template from heat.engine import template
from heat.tests import common from heat.tests import common
from heat.tests import utils from heat.tests import utils
from senlinclient.common import exc from openstack import exceptions
node_stack_template = """ node_stack_template = """
@ -127,7 +127,7 @@ class SenlinNodeTest(common.HeatTestCase):
def test_node_delete_success(self): def test_node_delete_success(self):
node = self._create_node() node = self._create_node()
self.senlin_mock.get_node.side_effect = [ self.senlin_mock.get_node.side_effect = [
exc.sdkexc.ResourceNotFound('SenlinNode'), exceptions.ResourceNotFound('SenlinNode'),
] ]
scheduler.TaskRunner(node.delete)() scheduler.TaskRunner(node.delete)()
self.senlin_mock.delete_node.assert_called_once_with( self.senlin_mock.delete_node.assert_called_once_with(

View File

@ -15,8 +15,8 @@
import copy import copy
import mock import mock
from openstack import exceptions
from oslo_config import cfg from oslo_config import cfg
from senlinclient.common import exc
from heat.common import exception from heat.common import exception
from heat.common import template_format from heat.common import template_format
@ -134,7 +134,7 @@ class SenlinPolicyTest(common.HeatTestCase):
'action': 'fake_action'} 'action': 'fake_action'}
policy = self._create_policy(self.t) policy = self._create_policy(self.t)
self.senlin_mock.get_policy.side_effect = [ self.senlin_mock.get_policy.side_effect = [
exc.sdkexc.ResourceNotFound('SenlinPolicy'), exceptions.ResourceNotFound('SenlinPolicy'),
] ]
scheduler.TaskRunner(policy.delete)() scheduler.TaskRunner(policy.delete)()
self.senlin_mock.cluster_detach_policy.assert_called_once_with( self.senlin_mock.cluster_detach_policy.assert_called_once_with(
@ -145,10 +145,10 @@ class SenlinPolicyTest(common.HeatTestCase):
def test_policy_delete_not_attached(self): def test_policy_delete_not_attached(self):
policy = self._create_policy(self.t) policy = self._create_policy(self.t)
self.senlin_mock.get_policy.side_effect = [ self.senlin_mock.get_policy.side_effect = [
exc.sdkexc.ResourceNotFound('SenlinPolicy'), exceptions.ResourceNotFound('SenlinPolicy'),
] ]
self.senlin_mock.cluster_detach_policy.side_effect = [ self.senlin_mock.cluster_detach_policy.side_effect = [
exc.sdkexc.HttpException(http_status=400), exceptions.HttpException(http_status=400),
] ]
scheduler.TaskRunner(policy.delete)() scheduler.TaskRunner(policy.delete)()
self.senlin_mock.cluster_detach_policy.assert_called_once_with( self.senlin_mock.cluster_detach_policy.assert_called_once_with(

View File

@ -14,7 +14,7 @@
import mock import mock
from senlinclient.common import exc from openstack import exceptions
from heat.common import template_format from heat.common import template_format
from heat.engine.clients.os import senlin from heat.engine.clients.os import senlin
@ -106,7 +106,7 @@ class SenlinReceiverTest(common.HeatTestCase):
def test_recv_delete_not_found(self): def test_recv_delete_not_found(self):
self.senlin_mock.delete_receiver.side_effect = [ self.senlin_mock.delete_receiver.side_effect = [
exc.sdkexc.ResourceNotFound(http_status=404) exceptions.ResourceNotFound(http_status=404)
] ]
recv = self._create_recv(self.t) recv = self._create_recv(self.t)
scheduler.TaskRunner(recv.delete)() scheduler.TaskRunner(recv.delete)()

View File

@ -46,7 +46,6 @@ python-novaclient>=9.1.0 # Apache-2.0
python-octaviaclient>=1.3.0 # Apache-2.0 python-octaviaclient>=1.3.0 # Apache-2.0
python-openstackclient>=3.12.0 # Apache-2.0 python-openstackclient>=3.12.0 # Apache-2.0
python-saharaclient>=1.4.0 # Apache-2.0 python-saharaclient>=1.4.0 # Apache-2.0
python-senlinclient>=1.1.0 # Apache-2.0
python-swiftclient>=3.2.0 # Apache-2.0 python-swiftclient>=3.2.0 # Apache-2.0
python-troveclient>=2.2.0 # Apache-2.0 python-troveclient>=2.2.0 # Apache-2.0
python-zaqarclient>=1.0.0 # Apache-2.0 python-zaqarclient>=1.0.0 # Apache-2.0