Port rackspace clients to client plugins
This change converts the rackspace client code to be contributed via client plugins. This is the last change required for all in-tree clients to be contributed via plugins. A RackspaceTest base class which adds the rackspace directory to the client plugins path which ensures any rackspace variants are loaded. Plugin loading precedence is used to ensure rackspace versions will override core plugins with the same name. The nova plugin will fallback to returning the core nova client so that CloudServersTest will run even when pyrax is not installed. The cloud_backed configuration option will no longer have any effect on whether rackspace clients will be loaded. Instead they will be loaded by stevedore via the setup.cfg heat.clients entry point. Change-Id: I312de40064cc00896dba1835117ff67003e57938
This commit is contained in:
parent
0eb93cbf33
commit
1a44187fdf
@ -18,140 +18,37 @@ import urlparse
|
||||
from oslo.config import cfg
|
||||
|
||||
from heat.common import exception
|
||||
from heat.engine import clients
|
||||
from heat.engine.clients import client_plugin
|
||||
from heat.engine.clients.os import cinder
|
||||
from heat.engine.clients.os import glance
|
||||
from heat.engine.clients.os import nova
|
||||
from heat.engine.clients.os import trove
|
||||
|
||||
from heat.openstack.common.gettextutils import _
|
||||
from heat.openstack.common import log as logging
|
||||
|
||||
from glanceclient import client as glanceclient
|
||||
from glanceclient import client as gc
|
||||
from troveclient import client as tc
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
import pyrax
|
||||
except ImportError:
|
||||
LOG.info(_('pyrax not available'))
|
||||
pyrax = None
|
||||
|
||||
|
||||
class Clients(clients.OpenStackClients):
|
||||
class RackspaceClientPlugin(client_plugin.ClientPlugin):
|
||||
|
||||
"""Convenience class to create and cache client instances."""
|
||||
|
||||
def __init__(self, context):
|
||||
super(Clients, self).__init__(context)
|
||||
self.pyrax = None
|
||||
pyrax = None
|
||||
|
||||
def _get_client(self, name):
|
||||
if not self.pyrax:
|
||||
self.__authenticate()
|
||||
if name not in self._clients:
|
||||
client = self.pyrax.get_client(
|
||||
name, cfg.CONF.region_name_for_services)
|
||||
self._clients[name] = client
|
||||
return self._clients[name]
|
||||
if self.pyrax is None:
|
||||
self._authenticate()
|
||||
return self.pyrax.get_client(
|
||||
name, cfg.CONF.region_name_for_services)
|
||||
|
||||
def auto_scale(self):
|
||||
"""Rackspace Auto Scale client."""
|
||||
return self._get_client("autoscale")
|
||||
|
||||
def cloud_lb(self):
|
||||
"""Rackspace cloud loadbalancer client."""
|
||||
return self._get_client("load_balancer")
|
||||
|
||||
def cloud_dns(self):
|
||||
"""Rackspace cloud dns client."""
|
||||
return self._get_client("dns")
|
||||
|
||||
def nova(self):
|
||||
"""Rackspace cloudservers client."""
|
||||
return self._get_client("compute")
|
||||
|
||||
def cloud_networks(self):
|
||||
"""
|
||||
Rackspace cloud networks client.
|
||||
|
||||
Though pyrax "fixed" the network client bugs that were introduced
|
||||
in 1.8, it still doesn't work for contexts because of caching of the
|
||||
nova client.
|
||||
"""
|
||||
if "networks" not in self._clients:
|
||||
if not self.pyrax:
|
||||
self.__authenticate()
|
||||
# need special handling now since the contextual
|
||||
# pyrax doesn't handle "networks" not being in
|
||||
# the catalog
|
||||
ep = pyrax._get_service_endpoint(
|
||||
self.pyrax,
|
||||
"compute",
|
||||
region=cfg.CONF.region_name_for_services)
|
||||
cls = pyrax._client_classes['compute:network']
|
||||
self._clients["networks"] = cls(
|
||||
self.pyrax,
|
||||
region_name=cfg.CONF.region_name_for_services,
|
||||
management_url=ep)
|
||||
return self._clients["networks"]
|
||||
|
||||
def trove(self):
|
||||
"""
|
||||
Rackspace trove client.
|
||||
|
||||
Since the pyrax module uses its own client implementation for Cloud
|
||||
Databases, we have to skip pyrax on this one and override the super
|
||||
management url to be region-aware.
|
||||
"""
|
||||
if "trove" not in self._clients:
|
||||
super(Clients, self).trove(service_type='rax:database')
|
||||
management_url = self.url_for(
|
||||
service_type='rax:database',
|
||||
region_name=cfg.CONF.region_name_for_services)
|
||||
self._clients['trove'].client.management_url = management_url
|
||||
return self._clients['trove']
|
||||
|
||||
def cinder(self):
|
||||
"""Override the region for the cinder client."""
|
||||
if "cinder" not in self._clients:
|
||||
super(Clients, self).cinder()
|
||||
management_url = self.url_for(
|
||||
service_type='volume',
|
||||
region_name=cfg.CONF.region_name_for_services)
|
||||
self._clients['cinder'].client.management_url = management_url
|
||||
return self._clients['cinder']
|
||||
|
||||
def swift(self):
|
||||
# Rackspace doesn't include object-store in the default catalog
|
||||
# for "reasons". The pyrax client takes care of this, but it
|
||||
# returns a wrapper over the upstream python-swiftclient so we
|
||||
# unwrap here and things just work
|
||||
return self._get_client("object_store").connection
|
||||
|
||||
def glance(self):
|
||||
if "image" not in self._clients:
|
||||
con = self.context
|
||||
endpoint_type = self._get_client_option('glance', 'endpoint_type')
|
||||
endpoint = self.url_for(
|
||||
service_type='image',
|
||||
endpoint_type=endpoint_type,
|
||||
region_name=cfg.CONF.region_name_for_services)
|
||||
# Rackspace service catalog includes a tenant scoped glance
|
||||
# endpoint so we have to munge the url a bit
|
||||
glance_url = urlparse.urlparse(endpoint)
|
||||
# remove the tenant and following from the url
|
||||
endpoint = "%s://%s" % (glance_url.scheme, glance_url.hostname)
|
||||
args = {
|
||||
'auth_url': con.auth_url,
|
||||
'service_type': 'image',
|
||||
'project_id': con.tenant,
|
||||
'token': self.auth_token,
|
||||
'endpoint_type': endpoint_type,
|
||||
'ca_file': self._get_client_option('glance', 'ca_file'),
|
||||
'cert_file': self._get_client_option('glance', 'cert_file'),
|
||||
'key_file': self._get_client_option('glance', 'key_file'),
|
||||
'insecure': self._get_client_option('glance', 'insecure')
|
||||
}
|
||||
client = glanceclient.Client('2', endpoint, **args)
|
||||
self._clients["image"] = client
|
||||
return self._clients["image"]
|
||||
|
||||
def __authenticate(self):
|
||||
def _authenticate(self):
|
||||
"""Create an authenticated client context."""
|
||||
self.pyrax = pyrax.create_context("rackspace")
|
||||
self.pyrax.auth_endpoint = self.context.auth_url
|
||||
@ -167,3 +64,145 @@ class Clients(clients.OpenStackClients):
|
||||
raise exception.AuthorizationFailure()
|
||||
LOG.info(_("User %s authenticated successfully."),
|
||||
self.context.username)
|
||||
|
||||
|
||||
class RackspaceAutoScaleClient(RackspaceClientPlugin):
|
||||
|
||||
def _create(self):
|
||||
"""Rackspace Auto Scale client."""
|
||||
return self._get_client("autoscale")
|
||||
|
||||
|
||||
class RackspaceCloudLBClient(RackspaceClientPlugin):
|
||||
|
||||
def _create(self):
|
||||
"""Rackspace cloud loadbalancer client."""
|
||||
return self._get_client("load_balancer")
|
||||
|
||||
|
||||
class RackspaceCloudDNSClient(RackspaceClientPlugin):
|
||||
|
||||
def _create(self):
|
||||
"""Rackspace cloud dns client."""
|
||||
return self._get_client("dns")
|
||||
|
||||
|
||||
class RackspaceNovaClient(nova.NovaClientPlugin,
|
||||
RackspaceClientPlugin):
|
||||
|
||||
def _create(self):
|
||||
"""Rackspace cloudservers client."""
|
||||
client = self._get_client("compute")
|
||||
if not client:
|
||||
client = super(RackspaceNovaClient, self)._create()
|
||||
return client
|
||||
|
||||
|
||||
class RackspaceCloudNetworksClient(RackspaceClientPlugin):
|
||||
|
||||
def _create(self):
|
||||
"""
|
||||
Rackspace cloud networks client.
|
||||
|
||||
Though pyrax "fixed" the network client bugs that were introduced
|
||||
in 1.8, it still doesn't work for contexts because of caching of the
|
||||
nova client.
|
||||
"""
|
||||
if not self.pyrax:
|
||||
self._authenticate()
|
||||
# need special handling now since the contextual
|
||||
# pyrax doesn't handle "networks" not being in
|
||||
# the catalog
|
||||
ep = pyrax._get_service_endpoint(
|
||||
self.pyrax, "compute", region=cfg.CONF.region_name_for_services)
|
||||
cls = pyrax._client_classes['compute:network']
|
||||
client = cls(self.pyrax,
|
||||
region_name=cfg.CONF.region_name_for_services,
|
||||
management_url=ep)
|
||||
return client
|
||||
|
||||
|
||||
class RackspaceTroveClient(trove.TroveClientPlugin):
|
||||
"""
|
||||
Rackspace trove client.
|
||||
|
||||
Since the pyrax module uses its own client implementation for Cloud
|
||||
Databases, we have to skip pyrax on this one and override the super
|
||||
implementation to account for custom service type and regionalized
|
||||
management url.
|
||||
"""
|
||||
|
||||
def _create(self):
|
||||
service_type = "rax:database"
|
||||
con = self.context
|
||||
endpoint_type = self._get_client_option('trove', 'endpoint_type')
|
||||
args = {
|
||||
'service_type': service_type,
|
||||
'auth_url': con.auth_url,
|
||||
'proxy_token': con.auth_token,
|
||||
'username': None,
|
||||
'password': None,
|
||||
'cacert': self._get_client_option('trove', 'ca_file'),
|
||||
'insecure': self._get_client_option('trove', 'insecure'),
|
||||
'endpoint_type': endpoint_type
|
||||
}
|
||||
|
||||
client = tc.Client('1.0', **args)
|
||||
region = cfg.CONF.region_name_for_services
|
||||
management_url = self.url_for(service_type=service_type,
|
||||
endpoint_type=endpoint_type,
|
||||
region_name=region)
|
||||
client.client.auth_token = con.auth_token
|
||||
client.client.management_url = management_url
|
||||
|
||||
return client
|
||||
|
||||
|
||||
class RackspaceCinderClient(cinder.CinderClientPlugin):
|
||||
|
||||
def _create(self):
|
||||
"""Override the region for the cinder client."""
|
||||
client = super(RackspaceCinderClient, self)._create()
|
||||
management_url = self.url_for(
|
||||
service_type='volume',
|
||||
region_name=cfg.CONF.region_name_for_services)
|
||||
client.client.management_url = management_url
|
||||
return client
|
||||
|
||||
|
||||
class RackspaceSwiftClient(RackspaceClientPlugin):
|
||||
|
||||
def _create(self):
|
||||
# Rackspace doesn't include object-store in the default catalog
|
||||
# for "reasons". The pyrax client takes care of this, but it
|
||||
# returns a wrapper over the upstream python-swiftclient so we
|
||||
# unwrap here and things just work
|
||||
return self._get_client("object_store").connection
|
||||
|
||||
|
||||
class RackspaceGlanceClient(glance.GlanceClientPlugin):
|
||||
|
||||
def _create(self):
|
||||
con = self.context
|
||||
endpoint_type = self._get_client_option('glance', 'endpoint_type')
|
||||
endpoint = self.url_for(
|
||||
service_type='image',
|
||||
endpoint_type=endpoint_type,
|
||||
region_name=cfg.CONF.region_name_for_services)
|
||||
# Rackspace service catalog includes a tenant scoped glance
|
||||
# endpoint so we have to munge the url a bit
|
||||
glance_url = urlparse.urlparse(endpoint)
|
||||
# remove the tenant and following from the url
|
||||
endpoint = "%s://%s" % (glance_url.scheme, glance_url.hostname)
|
||||
args = {
|
||||
'auth_url': con.auth_url,
|
||||
'service_type': 'image',
|
||||
'project_id': con.tenant,
|
||||
'token': self.auth_token,
|
||||
'endpoint_type': endpoint_type,
|
||||
'ca_file': self._get_client_option('glance', 'ca_file'),
|
||||
'cert_file': self._get_client_option('glance', 'cert_file'),
|
||||
'key_file': self._get_client_option('glance', 'key_file'),
|
||||
'insecure': self._get_client_option('glance', 'insecure')
|
||||
}
|
||||
return gc.Client('2', endpoint, **args)
|
||||
|
@ -306,13 +306,13 @@ class Group(resource.Resource):
|
||||
|
||||
The resource_id is set to the resulting group's ID.
|
||||
"""
|
||||
asclient = self.stack.clients.auto_scale()
|
||||
asclient = self.auto_scale()
|
||||
group = asclient.create(**self._get_create_args())
|
||||
self.resource_id_set(str(group.id))
|
||||
|
||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||
"""Update the group configuration and the launch configuration."""
|
||||
asclient = self.stack.clients.auto_scale()
|
||||
asclient = self.auto_scale()
|
||||
if self.GROUP_CONFIGURATION in prop_diff:
|
||||
args = self._get_group_config_args(
|
||||
prop_diff[self.GROUP_CONFIGURATION])
|
||||
@ -332,7 +332,7 @@ class Group(resource.Resource):
|
||||
"""
|
||||
if self.resource_id is None:
|
||||
return
|
||||
asclient = self.stack.clients.auto_scale()
|
||||
asclient = self.auto_scale()
|
||||
args = self._get_group_config_args(
|
||||
self.properties[self.GROUP_CONFIGURATION])
|
||||
args['min_entities'] = 0
|
||||
@ -347,7 +347,7 @@ class Group(resource.Resource):
|
||||
if self.resource_id is None:
|
||||
return True
|
||||
try:
|
||||
self.stack.clients.auto_scale().delete(self.resource_id)
|
||||
self.auto_scale().delete(self.resource_id)
|
||||
except Forbidden:
|
||||
return False
|
||||
except NotFound:
|
||||
@ -355,6 +355,9 @@ class Group(resource.Resource):
|
||||
else:
|
||||
return True
|
||||
|
||||
def auto_scale(self):
|
||||
return self.client('auto_scale')
|
||||
|
||||
|
||||
class ScalingPolicy(resource.Resource):
|
||||
|
||||
@ -447,7 +450,7 @@ class ScalingPolicy(resource.Resource):
|
||||
|
||||
The resource ID is initialized to {group_id}:{policy_id}.
|
||||
"""
|
||||
asclient = self.stack.clients.auto_scale()
|
||||
asclient = self.auto_scale()
|
||||
args = self._get_args(self.properties)
|
||||
policy = asclient.add_policy(**args)
|
||||
resource_id = '%s:%s' % (self.properties[self.GROUP], policy.id)
|
||||
@ -457,14 +460,14 @@ class ScalingPolicy(resource.Resource):
|
||||
return self.resource_id.split(':', 1)[1]
|
||||
|
||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||
asclient = self.stack.clients.auto_scale()
|
||||
asclient = self.auto_scale()
|
||||
args = self._get_args(tmpl_diff['Properties'])
|
||||
args['policy'] = self._get_policy_id()
|
||||
asclient.replace_policy(**args)
|
||||
|
||||
def handle_delete(self):
|
||||
"""Delete the policy if it exists."""
|
||||
asclient = self.stack.clients.auto_scale()
|
||||
asclient = self.auto_scale()
|
||||
if self.resource_id is None:
|
||||
return
|
||||
policy_id = self._get_policy_id()
|
||||
@ -473,6 +476,9 @@ class ScalingPolicy(resource.Resource):
|
||||
except NotFound:
|
||||
pass
|
||||
|
||||
def auto_scale(self):
|
||||
return self.client('auto_scale')
|
||||
|
||||
|
||||
class WebHook(resource.Resource):
|
||||
|
||||
@ -534,7 +540,7 @@ class WebHook(resource.Resource):
|
||||
metadata=props.get(self.METADATA))
|
||||
|
||||
def handle_create(self):
|
||||
asclient = self.stack.clients.auto_scale()
|
||||
asclient = self.auto_scale()
|
||||
args = self._get_args(self.properties)
|
||||
webhook = asclient.add_webhook(**args)
|
||||
self.resource_id_set(webhook.id)
|
||||
@ -548,7 +554,7 @@ class WebHook(resource.Resource):
|
||||
self.data_set(key, url)
|
||||
|
||||
def handle_update(self, json_snippet, tmpl_diff, prop_diff):
|
||||
asclient = self.stack.clients.auto_scale()
|
||||
asclient = self.auto_scale()
|
||||
args = self._get_args(json_snippet['Properties'])
|
||||
args['webhook'] = self.resource_id
|
||||
asclient.replace_webhook(**args)
|
||||
@ -563,13 +569,16 @@ class WebHook(resource.Resource):
|
||||
def handle_delete(self):
|
||||
if self.resource_id is None:
|
||||
return
|
||||
asclient = self.stack.clients.auto_scale()
|
||||
asclient = self.auto_scale()
|
||||
group_id, policy_id = self.properties[self.POLICY].split(':', 1)
|
||||
try:
|
||||
asclient.delete_webhook(group_id, policy_id, self.resource_id)
|
||||
except NotFound:
|
||||
pass
|
||||
|
||||
def auto_scale(self):
|
||||
return self.client('auto_scale')
|
||||
|
||||
|
||||
def resource_mapping():
|
||||
return {
|
||||
|
@ -147,7 +147,7 @@ class CloudDns(resource.Resource):
|
||||
}
|
||||
|
||||
def cloud_dns(self):
|
||||
return self.stack.clients.cloud_dns()
|
||||
return self.client('cloud_dns')
|
||||
|
||||
def handle_create(self):
|
||||
"""Create a Rackspace CloudDns Instance."""
|
||||
|
@ -387,7 +387,7 @@ class CloudLoadBalancer(resource.Resource):
|
||||
self.clb = self.cloud_lb()
|
||||
|
||||
def cloud_lb(self):
|
||||
return self.stack.clients.cloud_lb()
|
||||
return self.client('cloud_lb')
|
||||
|
||||
def _setup_properties(self, properties, function):
|
||||
"""Use defined schema properties as kwargs for loadbalancer objects."""
|
||||
|
@ -98,7 +98,7 @@ class CloudNetwork(resource.Resource):
|
||||
return self._network
|
||||
|
||||
def cloud_networks(self):
|
||||
return self.stack.clients.cloud_networks()
|
||||
return self.client('cloud_networks')
|
||||
|
||||
def handle_create(self):
|
||||
cnw = self.cloud_networks().create(label=self.properties[self.LABEL],
|
||||
|
@ -16,11 +16,10 @@ import itertools
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common import template_format
|
||||
from heat.engine import clients
|
||||
from heat.engine import resource
|
||||
from heat.engine import rsrc_defn
|
||||
from heat.engine import scheduler
|
||||
from heat.tests.common import HeatTestCase
|
||||
from heat.tests import common
|
||||
from heat.tests import utils
|
||||
|
||||
from ..resources import auto_scale # noqa
|
||||
@ -165,7 +164,7 @@ class FakeAutoScale(object):
|
||||
webhook.kwargs['metadata'] = metadata
|
||||
|
||||
|
||||
class ScalingGroupTest(HeatTestCase):
|
||||
class ScalingGroupTest(common.HeatTestCase):
|
||||
|
||||
group_template = template_format.parse('''
|
||||
HeatTemplateFormatVersion: "2012-12-12"
|
||||
@ -204,8 +203,8 @@ class ScalingGroupTest(HeatTestCase):
|
||||
for res_name, res_class in auto_scale.resource_mapping().items():
|
||||
resource._register_class(res_name, res_class)
|
||||
self.fake_auto_scale = FakeAutoScale()
|
||||
self.patchobject(clients.OpenStackClients, 'auto_scale',
|
||||
return_value=self.fake_auto_scale, create=True)
|
||||
self.patchobject(auto_scale.Group, 'auto_scale',
|
||||
return_value=self.fake_auto_scale)
|
||||
|
||||
def _setup_test_stack(self):
|
||||
self.stack = utils.parse_stack(self.group_template)
|
||||
@ -382,7 +381,7 @@ Resources:
|
||||
if count < 3:
|
||||
raise auto_scale.Forbidden("Not empty!")
|
||||
|
||||
self.patchobject(self.fake_auto_scale, 'delete', new=delete)
|
||||
self.patchobject(self.fake_auto_scale, 'delete', side_effect=delete)
|
||||
resource = self.stack['my_group']
|
||||
scheduler.TaskRunner(resource.delete)()
|
||||
# It really called delete until it succeeded:
|
||||
@ -398,14 +397,14 @@ Resources:
|
||||
def delete(group_id):
|
||||
1 / 0
|
||||
|
||||
self.patchobject(self.fake_auto_scale, 'delete', new=delete)
|
||||
self.patchobject(self.fake_auto_scale, 'delete', side_effect=delete)
|
||||
resource = self.stack['my_group']
|
||||
err = self.assertRaises(
|
||||
exception.ResourceFailure, scheduler.TaskRunner(resource.delete))
|
||||
self.assertIsInstance(err.exc, ZeroDivisionError)
|
||||
|
||||
|
||||
class PolicyTest(HeatTestCase):
|
||||
class PolicyTest(common.HeatTestCase):
|
||||
policy_template = template_format.parse('''
|
||||
HeatTemplateFormatVersion: "2012-12-12"
|
||||
Description: "Rackspace Auto Scale"
|
||||
@ -426,8 +425,8 @@ class PolicyTest(HeatTestCase):
|
||||
for res_name, res_class in auto_scale.resource_mapping().items():
|
||||
resource._register_class(res_name, res_class)
|
||||
self.fake_auto_scale = FakeAutoScale()
|
||||
self.patchobject(clients.OpenStackClients, 'auto_scale',
|
||||
return_value=self.fake_auto_scale, create=True)
|
||||
self.patchobject(auto_scale.ScalingPolicy, 'auto_scale',
|
||||
return_value=self.fake_auto_scale)
|
||||
|
||||
def _setup_test_stack(self, template):
|
||||
self.stack = utils.parse_stack(template)
|
||||
@ -550,7 +549,7 @@ class PolicyTest(HeatTestCase):
|
||||
self.assertEqual({}, self.fake_auto_scale.policies)
|
||||
|
||||
|
||||
class WebHookTest(HeatTestCase):
|
||||
class WebHookTest(common.HeatTestCase):
|
||||
webhook_template = template_format.parse('''
|
||||
HeatTemplateFormatVersion: "2012-12-12"
|
||||
Description: "Rackspace Auto Scale"
|
||||
@ -570,8 +569,8 @@ class WebHookTest(HeatTestCase):
|
||||
for res_name, res_class in auto_scale.resource_mapping().items():
|
||||
resource._register_class(res_name, res_class)
|
||||
self.fake_auto_scale = FakeAutoScale()
|
||||
self.patchobject(clients.OpenStackClients, 'auto_scale',
|
||||
return_value=self.fake_auto_scale, create=True)
|
||||
self.patchobject(auto_scale.WebHook, 'auto_scale',
|
||||
return_value=self.fake_auto_scale)
|
||||
|
||||
def _setup_test_stack(self, template):
|
||||
self.stack = utils.parse_stack(template)
|
||||
|
@ -23,7 +23,7 @@ from heat.common import template_format
|
||||
from heat.engine import resource
|
||||
from heat.engine import rsrc_defn
|
||||
from heat.engine import scheduler
|
||||
from heat.tests.common import HeatTestCase
|
||||
from heat.tests import common
|
||||
from heat.tests import utils
|
||||
|
||||
from ..resources import cloud_loadbalancer as lb # noqa
|
||||
@ -155,7 +155,7 @@ def override_resource():
|
||||
}
|
||||
|
||||
|
||||
class LoadBalancerTest(HeatTestCase):
|
||||
class LoadBalancerTest(common.HeatTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(LoadBalancerTest, self).setUp()
|
||||
|
@ -20,7 +20,7 @@ from heat.common import exception
|
||||
from heat.common import template_format
|
||||
from heat.engine import resource
|
||||
from heat.engine import scheduler
|
||||
from heat.tests.common import HeatTestCase
|
||||
from heat.tests import common
|
||||
from heat.tests import utils
|
||||
|
||||
from ..resources import cloudnetworks # noqa
|
||||
@ -75,7 +75,7 @@ class FakeClient(object):
|
||||
|
||||
|
||||
@mock.patch.object(cloudnetworks.CloudNetwork, "cloud_networks")
|
||||
class CloudNetworkTest(HeatTestCase):
|
||||
class CloudNetworkTest(common.HeatTestCase):
|
||||
|
||||
_template = template_format.parse("""
|
||||
heat_template_version: 2013-05-23
|
||||
|
@ -1,29 +0,0 @@
|
||||
#
|
||||
# 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.
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from heat.engine import clients
|
||||
from heat.tests.common import HeatTestCase
|
||||
|
||||
from .. import clients as rackspace_clients # noqa
|
||||
|
||||
|
||||
class ClientsTest(HeatTestCase):
|
||||
def setUp(self):
|
||||
super(ClientsTest, self).setUp()
|
||||
cfg.CONF.set_override('cloud_backend', 'rackspace.clients.Clients')
|
||||
self.backend = clients.ClientBackend('fake_context')
|
||||
|
||||
def test_client_plugin_loads(self):
|
||||
self.assertIsInstance(self.backend, rackspace_clients.Clients)
|
@ -13,18 +13,17 @@
|
||||
|
||||
import mock
|
||||
import mox
|
||||
from oslo.config import cfg
|
||||
import six
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common import template_format
|
||||
from heat.engine.clients.os import glance
|
||||
from heat.engine.clients.os import nova
|
||||
from heat.engine import environment
|
||||
from heat.engine import parser
|
||||
from heat.engine import resource
|
||||
from heat.engine import scheduler
|
||||
from heat.openstack.common import uuidutils
|
||||
from heat.tests.common import HeatTestCase
|
||||
from heat.tests import common
|
||||
from heat.tests import utils
|
||||
from heat.tests.v1_1 import fakes
|
||||
|
||||
@ -56,34 +55,42 @@ wp_template = '''
|
||||
}
|
||||
'''
|
||||
|
||||
cfg.CONF.import_opt('region_name_for_services', 'heat.common.config')
|
||||
|
||||
class CloudServersTest(HeatTestCase):
|
||||
|
||||
class CloudServersTest(common.HeatTestCase):
|
||||
def setUp(self):
|
||||
super(CloudServersTest, self).setUp()
|
||||
cfg.CONF.set_override('region_name_for_services', 'RegionOne')
|
||||
self.ctx = utils.dummy_context()
|
||||
|
||||
self.fc = fakes.FakeClient()
|
||||
mock_nova_create = mock.Mock()
|
||||
self.ctx.clients.client_plugin(
|
||||
'nova')._create = mock_nova_create
|
||||
mock_nova_create.return_value = self.fc
|
||||
|
||||
self.stub_keystoneclient()
|
||||
# Test environment may not have pyrax client library installed and if
|
||||
# pyrax is not installed resource class would not be registered.
|
||||
# So register resource provider class explicitly for unit testing.
|
||||
resource._register_class("Rackspace::Cloud::Server",
|
||||
cloud_server.CloudServer)
|
||||
|
||||
def _mock_get_image_id_success(self, imageId_input, imageId):
|
||||
self.m.StubOutWithMock(glance.GlanceClientPlugin, 'get_image_id')
|
||||
glance.GlanceClientPlugin.get_image_id(imageId_input).MultipleTimes().\
|
||||
AndReturn(imageId)
|
||||
def _mock_get_image_id_success(self, imageId):
|
||||
self.mock_get_image = mock.Mock()
|
||||
self.ctx.clients.client_plugin(
|
||||
'glance').get_image_id = self.mock_get_image
|
||||
self.mock_get_image.return_value = imageId
|
||||
|
||||
def _stub_server_validate(self, server, imageId_input, image_id):
|
||||
# stub nova validate
|
||||
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
|
||||
nova.NovaClientPlugin._create().MultipleTimes().AndReturn(self.fc)
|
||||
|
||||
# stub glance image validate
|
||||
self._mock_get_image_id_success(imageId_input, image_id)
|
||||
self._mock_get_image_id_success(image_id)
|
||||
|
||||
def _setup_test_stack(self, stack_name):
|
||||
t = template_format.parse(wp_template)
|
||||
template = parser.Template(t)
|
||||
stack = parser.Stack(utils.dummy_context(), stack_name, template,
|
||||
stack = parser.Stack(self.ctx, stack_name, template,
|
||||
environment.Environment({'key_name': 'test'}),
|
||||
stack_id=uuidutils.generate_uuid())
|
||||
return (template, stack)
|
||||
@ -317,11 +324,9 @@ class CloudServersTest(HeatTestCase):
|
||||
self.assertEqual('Error: Unknown Managed Cloud automation status: FOO',
|
||||
six.text_type(exc))
|
||||
|
||||
@mock.patch.object(nova.NovaClientPlugin, '_create')
|
||||
@mock.patch.object(resource.Resource, 'data_set')
|
||||
def test_create_store_admin_pass_resource_data(self,
|
||||
mock_data_set,
|
||||
mock_create):
|
||||
mock_data_set):
|
||||
self._mock_metadata_os_distro()
|
||||
return_server = self.fc.servers.list()[1]
|
||||
return_server.adminPass = 'autogenerated'
|
||||
@ -333,19 +338,15 @@ class CloudServersTest(HeatTestCase):
|
||||
server = cloud_server.CloudServer('WebServer',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
mock_create.return_value = self.fc
|
||||
self.fc.servers.create = mock.Mock(return_value=return_server)
|
||||
self._mock_get_image_id_success('CentOS 5.2', 'image_id')
|
||||
self._mock_get_image_id_success('image_id')
|
||||
scheduler.TaskRunner(server.create)()
|
||||
expected_call = mock.call(server.ADMIN_PASS,
|
||||
'autogenerated', redact=True)
|
||||
self.assertIn(expected_call, mock_data_set.call_args_list)
|
||||
|
||||
@mock.patch.object(nova.NovaClientPlugin, '_create')
|
||||
@mock.patch.object(resource.Resource, 'data_set')
|
||||
def test_create_save_admin_pass_is_false(self,
|
||||
mock_data_set,
|
||||
mock_create):
|
||||
def test_create_save_admin_pass_is_false(self, mock_data_set):
|
||||
self._mock_metadata_os_distro()
|
||||
return_server = self.fc.servers.list()[1]
|
||||
return_server.adminPass = 'autogenerated'
|
||||
@ -357,19 +358,16 @@ class CloudServersTest(HeatTestCase):
|
||||
server = cloud_server.CloudServer('WebServer',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
mock_create.return_value = self.fc
|
||||
self.fc.servers.create = mock.Mock(return_value=return_server)
|
||||
self._mock_get_image_id_success('CentOS 5.2', 'image_id')
|
||||
self._mock_get_image_id_success('image_id')
|
||||
scheduler.TaskRunner(server.create)()
|
||||
expected_call = mock.call(mock.ANY, server.ADMIN_PASS,
|
||||
mock.ANY, mock.ANY)
|
||||
self.assertNotIn(expected_call, mock_data_set.call_args_list)
|
||||
|
||||
@mock.patch.object(nova.NovaClientPlugin, '_create')
|
||||
@mock.patch.object(resource.Resource, 'data_set')
|
||||
def test_create_save_admin_pass_defaults_to_false(self,
|
||||
mock_data_set,
|
||||
mock_create):
|
||||
mock_data_set):
|
||||
self._mock_metadata_os_distro()
|
||||
return_server = self.fc.servers.list()[1]
|
||||
return_server.adminPass = 'autogenerated'
|
||||
@ -381,19 +379,17 @@ class CloudServersTest(HeatTestCase):
|
||||
server = cloud_server.CloudServer('WebServer',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
mock_create.return_value = self.fc
|
||||
self.fc.servers.create = mock.Mock(return_value=return_server)
|
||||
self._mock_get_image_id_success('CentOS 5.2', 'image_id')
|
||||
self._mock_get_image_id_success('image_id')
|
||||
|
||||
scheduler.TaskRunner(server.create)()
|
||||
expected_call = mock.call(mock.ANY, server.ADMIN_PASS,
|
||||
mock.ANY, mock.ANY)
|
||||
self.assertNotIn(expected_call, mock_data_set.call_args_list)
|
||||
|
||||
@mock.patch.object(nova.NovaClientPlugin, '_create')
|
||||
@mock.patch.object(resource.Resource, 'data_set')
|
||||
def test_create_without_adminPass_attribute(self,
|
||||
mock_data_set,
|
||||
mock_create):
|
||||
mock_data_set):
|
||||
self._mock_metadata_os_distro()
|
||||
return_server = self.fc.servers.list()[1]
|
||||
stack_name = 'admin_pass_s'
|
||||
@ -403,9 +399,9 @@ class CloudServersTest(HeatTestCase):
|
||||
server = cloud_server.CloudServer('WebServer',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
mock_create.return_value = self.fc
|
||||
self.fc.servers.create = mock.Mock(return_value=return_server)
|
||||
self._mock_get_image_id_success('CentOS 5.2', 'image_id')
|
||||
self._mock_get_image_id_success('image_id')
|
||||
|
||||
scheduler.TaskRunner(server.create)()
|
||||
expected_call = mock.call(mock.ANY, server.ADMIN_PASS,
|
||||
mock.ANY, redact=mock.ANY)
|
||||
@ -433,9 +429,7 @@ class CloudServersTest(HeatTestCase):
|
||||
resource_defns['WebServer'], stack)
|
||||
self.assertEqual('foo', server.FnGetAtt('admin_pass'))
|
||||
|
||||
@mock.patch.object(nova.NovaClientPlugin, '_create')
|
||||
def _test_server_config_drive(self, user_data, config_drive, result,
|
||||
mock_create):
|
||||
def _test_server_config_drive(self, user_data, config_drive, result):
|
||||
return_server = self.fc.servers.list()[1]
|
||||
stack_name = 'no_user_data'
|
||||
(tmpl, stack) = self._setup_test_stack(stack_name)
|
||||
@ -445,11 +439,10 @@ class CloudServersTest(HeatTestCase):
|
||||
resource_defns = tmpl.resource_definitions(stack)
|
||||
server = cloud_server.CloudServer('WebServer',
|
||||
resource_defns['WebServer'], stack)
|
||||
mock_create.return_value = self.fc
|
||||
mock_servers_create = mock.Mock(return_value=return_server)
|
||||
self.fc.servers.create = mock_servers_create
|
||||
image_id = mock.ANY
|
||||
self._mock_get_image_id_success('CentOS 5.2', image_id)
|
||||
self._mock_get_image_id_success(image_id)
|
||||
scheduler.TaskRunner(server.create)()
|
||||
mock_servers_create.assert_called_with(
|
||||
image=image_id,
|
||||
|
@ -18,10 +18,26 @@ classifier =
|
||||
Programming Language :: Python :: 2.6
|
||||
|
||||
[files]
|
||||
# Copy to /usr/lib/heat for plugin loading
|
||||
packages =
|
||||
rackspace
|
||||
|
||||
# Copy to /usr/lib/heat for non-stevedore plugin loading
|
||||
data_files =
|
||||
lib/heat/rackspace = rackspace/resources/*
|
||||
|
||||
[entry_points]
|
||||
|
||||
heat.clients =
|
||||
auto_scale = rackspace.clients:RackspaceAutoScaleClient
|
||||
cinder = rackspace.clients:RackspaceCinderClient
|
||||
cloud_dns = rackspace.clients:RackspaceCloudDNSClient
|
||||
cloud_lb = rackspace.clients:RackspaceCloudLBClient
|
||||
cloud_networks = rackspace.clients:RackspaceCloudNetworksClient
|
||||
glance = rackspace.clients:RackspaceGlanceClient
|
||||
nova = rackspace.clients:RackspaceNovaClient
|
||||
trove = rackspace.clients:RackspaceTroveClient
|
||||
swift = rackspace.clients:RackspaceSwiftClient
|
||||
|
||||
[global]
|
||||
setup-hooks =
|
||||
pbr.hooks.setup_hook
|
||||
|
Loading…
x
Reference in New Issue
Block a user