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-zaqarclient (messaging 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
# under the License.
from openstack.config import cloud_region
from openstack import connection
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 import constraints
import heat.version
CLIENT_NAME = 'openstack'
@ -25,24 +28,39 @@ class OpenStackSDKPlugin(client_plugin.ClientPlugin):
exceptions_module = exceptions
service_types = [NETWORK] = ['network']
service_client_map = {NETWORK: 'neutron'}
api_version_map = {NETWORK: '2.0'}
service_types = [NETWORK, CLUSTERING] = ['network', 'clustering']
def _create(self, version=None):
prof = profile.Profile()
for svc_type in self.service_types:
interface = self._get_client_option(
self.service_client_map[svc_type], 'endpoint_type')
prof.set_interface(svc_type, interface)
prof.set_region(svc_type, self._get_region_name())
prof.set_version(svc_type, self.api_version_map[svc_type])
config = cloud_region.from_session(
# TODO(mordred) The way from_session calculates a cloud name
# doesn't interact well with the mocks in the test cases. The
# name is used in logging to distinguish requests made to different
# clouds. For now, set it to local - but maybe find a way to set
# it to something more meaningful later.
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
return connection.Connection(authenticator=key_session.auth,
verify=key_session.verify,
cert=key_session.cert,
profile=prof)
def _get_service_interfaces(self):
interfaces = {}
if not os_service_types:
return interfaces
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):
return isinstance(ex, exceptions.ResourceNotFound)

View File

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

View File

@ -12,7 +12,7 @@
# under the License.
import mock
from senlinclient.common import exc
from openstack import exceptions
from heat.engine.clients.os import senlin as senlin_plugin
from heat.tests import common
@ -32,10 +32,10 @@ class SenlinClientPluginTest(common.HeatTestCase):
def test_is_bad_request(self):
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(
exc.sdkexc.HttpException(http_status=404)))
exceptions.HttpException(http_status=404)))
def test_check_action_success(self):
mock_action = mock.MagicMock()
@ -87,10 +87,10 @@ class ProfileConstraintTest(common.HeatTestCase):
self.assertTrue(self.constraint.validate("PROFILE_ID", self.ctx))
def test_validate_false(self):
self.mock_get_profile.side_effect = exc.sdkexc.ResourceNotFound(
self.mock_get_profile.side_effect = exceptions.ResourceNotFound(
'PROFILE_ID')
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')
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))
def test_validate_false(self):
self.mock_get_cluster.side_effect = exc.sdkexc.ResourceNotFound(
self.mock_get_cluster.side_effect = exceptions.ResourceNotFound(
'CLUSTER_ID')
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')
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))
def test_validate_false(self):
self.mock_get_policy.side_effect = exc.sdkexc.ResourceNotFound(
self.mock_get_policy.side_effect = exceptions.ResourceNotFound(
'POLICY_ID')
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')
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.tests import common
from heat.tests import utils
from senlinclient.common import exc
from openstack import exceptions
cluster_stack_template = """
@ -165,7 +165,7 @@ class SenlinClusterTest(common.HeatTestCase):
def test_cluster_delete_success(self):
cluster = self._create_cluster(self.t)
self.senlin_mock.get_cluster.side_effect = [
exc.sdkexc.ResourceNotFound('SenlinCluster'),
exceptions.ResourceNotFound('SenlinCluster'),
]
scheduler.TaskRunner(cluster.delete)()
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.tests import common
from heat.tests import utils
from senlinclient.common import exc
from openstack import exceptions
node_stack_template = """
@ -127,7 +127,7 @@ class SenlinNodeTest(common.HeatTestCase):
def test_node_delete_success(self):
node = self._create_node()
self.senlin_mock.get_node.side_effect = [
exc.sdkexc.ResourceNotFound('SenlinNode'),
exceptions.ResourceNotFound('SenlinNode'),
]
scheduler.TaskRunner(node.delete)()
self.senlin_mock.delete_node.assert_called_once_with(

View File

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

View File

@ -14,7 +14,7 @@
import mock
from senlinclient.common import exc
from openstack import exceptions
from heat.common import template_format
from heat.engine.clients.os import senlin
@ -106,7 +106,7 @@ class SenlinReceiverTest(common.HeatTestCase):
def test_recv_delete_not_found(self):
self.senlin_mock.delete_receiver.side_effect = [
exc.sdkexc.ResourceNotFound(http_status=404)
exceptions.ResourceNotFound(http_status=404)
]
recv = self._create_recv(self.t)
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-openstackclient>=3.12.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-troveclient>=2.2.0 # Apache-2.0
python-zaqarclient>=1.0.0 # Apache-2.0