Move nova_utils functions to nova client plugin

All uses of nova_utils functions have been migrated to using
the nova client plugin version.

Existing functions now have a deprecation warning which is
suppressed during nova_utils unit tests.

nova_utils.get_flavor_id was being called with the trove
client, so there is now a dedicated trove plugin method
get_flavor_id.

Change-Id: Ic8120022a1f9117c783f6422069f94614bcbb3c6
This commit is contained in:
Steve Baker 2014-06-23 08:25:36 +12:00 committed by Randall Burt
parent cbcfe1cedf
commit 60e6eeb075
20 changed files with 815 additions and 170 deletions

View File

@ -16,7 +16,6 @@ import copy
from heat.common import exception
from heat.engine import attributes
from heat.engine import properties
from heat.engine.resources import nova_utils
from heat.engine.resources import server
from heat.openstack.common.gettextutils import _
from heat.openstack.common import log as logging
@ -200,7 +199,7 @@ class CloudServer(server.Server):
if not self._check_active(server):
return False
nova_utils.refresh_server(server)
self.client_plugin().refresh_server(server)
if 'rack_connect' in self.context.roles and not \
self._check_rack_connect_complete(server):
@ -216,7 +215,7 @@ class CloudServer(server.Server):
if name == self.DISTRO:
return self.distro
if name == self.PRIVATE_IP_V4:
return nova_utils.get_ip(self.server, 'private', 4)
return self.client_plugin().get_ip(self.server, 'private', 4)
if name == self.ADMIN_PASS_ATTR:
return self.data().get(self.ADMIN_PASS_ATTR, '')
return super(CloudServer, self)._resolve_attribute(name)

View File

@ -11,15 +11,42 @@
# License for the specific language governing permissions and limitations
# under the License.
import email
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import json
import logging
import os
import pkgutil
import string
from novaclient import client as nc
from novaclient import exceptions
from novaclient import shell as novashell
from oslo.config import cfg
import six
from six.moves.urllib import parse as urlparse
from heat.common import exception
from heat.engine.clients import client_plugin
from heat.engine import scheduler
LOG = logging.getLogger(__name__)
class NovaClientPlugin(client_plugin.ClientPlugin):
deferred_server_statuses = ['BUILD',
'HARD_REBOOT',
'PASSWORD',
'REBOOT',
'RESCUE',
'RESIZE',
'REVERT_RESIZE',
'SHUTOFF',
'SUSPENDED',
'VERIFY_RESIZE']
exceptions_module = exceptions
def _create(self):
@ -58,3 +85,307 @@ class NovaClientPlugin(client_plugin.ClientPlugin):
def is_bad_request(self, ex):
return isinstance(ex, exceptions.BadRequest)
def refresh_server(self, server):
'''
Refresh server's attributes and log warnings for non-critical
API errors.
'''
try:
server.get()
except exceptions.OverLimit as exc:
msg = _("Server %(name)s (%(id)s) received an OverLimit "
"response during server.get(): %(exception)s")
LOG.warning(msg % {'name': server.name,
'id': server.id,
'exception': exc})
except exceptions.ClientException as exc:
if ((getattr(exc, 'http_status', getattr(exc, 'code', None)) in
(500, 503))):
msg = _('Server "%(name)s" (%(id)s) received the following '
'exception during server.get(): %(exception)s')
LOG.warning(msg % {'name': server.name,
'id': server.id,
'exception': exc})
else:
raise
def get_ip(self, server, net_type, ip_version):
"""Return the server's IP of the given type and version."""
if net_type in server.addresses:
for ip in server.addresses[net_type]:
if ip['version'] == ip_version:
return ip['addr']
def get_flavor_id(self, flavor):
'''
Get the id for the specified flavor name.
If the specified value is flavor id, just return it.
:param flavor: the name of the flavor to find
:returns: the id of :flavor:
:raises: exception.FlavorMissing
'''
flavor_id = None
flavor_list = self.client().flavors.list()
for o in flavor_list:
if o.name == flavor:
flavor_id = o.id
break
if o.id == flavor:
flavor_id = o.id
break
if flavor_id is None:
raise exception.FlavorMissing(flavor_id=flavor)
return flavor_id
def get_keypair(self, key_name):
'''
Get the public key specified by :key_name:
:param key_name: the name of the key to look for
:returns: the keypair (name, public_key) for :key_name:
:raises: exception.UserKeyPairMissing
'''
try:
return self.client().keypairs.get(key_name)
except exceptions.NotFound:
raise exception.UserKeyPairMissing(key_name=key_name)
def build_userdata(self, metadata, userdata=None, instance_user=None,
user_data_format='HEAT_CFNTOOLS'):
'''
Build multipart data blob for CloudInit which includes user-supplied
Metadata, user data, and the required Heat in-instance configuration.
:param resource: the resource implementation
:type resource: heat.engine.Resource
:param userdata: user data string
:type userdata: str or None
:param instance_user: the user to create on the server
:type instance_user: string
:param user_data_format: Format of user data to return
:type user_data_format: string
:returns: multipart mime as a string
'''
if user_data_format == 'RAW':
return userdata
is_cfntools = user_data_format == 'HEAT_CFNTOOLS'
is_software_config = user_data_format == 'SOFTWARE_CONFIG'
def make_subpart(content, filename, subtype=None):
if subtype is None:
subtype = os.path.splitext(filename)[0]
msg = MIMEText(content, _subtype=subtype)
msg.add_header('Content-Disposition', 'attachment',
filename=filename)
return msg
def read_cloudinit_file(fn):
return pkgutil.get_data('heat', 'cloudinit/%s' % fn)
if instance_user:
config_custom_user = 'user: %s' % instance_user
# FIXME(shadower): compatibility workaround for cloud-init 0.6.3.
# We can drop this once we stop supporting 0.6.3 (which ships
# with Ubuntu 12.04 LTS).
#
# See bug https://bugs.launchpad.net/heat/+bug/1257410
boothook_custom_user = r"""useradd -m %s
echo -e '%s\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers
""" % (instance_user, instance_user)
else:
config_custom_user = ''
boothook_custom_user = ''
cloudinit_config = string.Template(
read_cloudinit_file('config')).safe_substitute(
add_custom_user=config_custom_user)
cloudinit_boothook = string.Template(
read_cloudinit_file('boothook.sh')).safe_substitute(
add_custom_user=boothook_custom_user)
attachments = [(cloudinit_config, 'cloud-config'),
(cloudinit_boothook, 'boothook.sh', 'cloud-boothook'),
(read_cloudinit_file('part_handler.py'),
'part-handler.py')]
if is_cfntools:
attachments.append((userdata, 'cfn-userdata', 'x-cfninitdata'))
elif is_software_config:
# attempt to parse userdata as a multipart message, and if it
# is, add each part as an attachment
userdata_parts = None
try:
userdata_parts = email.message_from_string(userdata)
except Exception:
pass
if userdata_parts and userdata_parts.is_multipart():
for part in userdata_parts.get_payload():
attachments.append((part.get_payload(),
part.get_filename(),
part.get_content_subtype()))
else:
attachments.append((userdata, 'userdata', 'x-shellscript'))
if is_cfntools:
attachments.append((read_cloudinit_file('loguserdata.py'),
'loguserdata.py', 'x-shellscript'))
if metadata:
attachments.append((json.dumps(metadata),
'cfn-init-data', 'x-cfninitdata'))
attachments.append((cfg.CONF.heat_watch_server_url,
'cfn-watch-server', 'x-cfninitdata'))
if is_cfntools:
attachments.append((cfg.CONF.heat_metadata_server_url,
'cfn-metadata-server', 'x-cfninitdata'))
# Create a boto config which the cfntools on the host use to know
# where the cfn and cw API's are to be accessed
cfn_url = urlparse.urlparse(cfg.CONF.heat_metadata_server_url)
cw_url = urlparse.urlparse(cfg.CONF.heat_watch_server_url)
is_secure = cfg.CONF.instance_connection_is_secure
vcerts = cfg.CONF.instance_connection_https_validate_certificates
boto_cfg = "\n".join(["[Boto]",
"debug = 0",
"is_secure = %s" % is_secure,
"https_validate_certificates = %s" % vcerts,
"cfn_region_name = heat",
"cfn_region_endpoint = %s" %
cfn_url.hostname,
"cloudwatch_region_name = heat",
"cloudwatch_region_endpoint = %s" %
cw_url.hostname])
attachments.append((boto_cfg,
'cfn-boto-cfg', 'x-cfninitdata'))
subparts = [make_subpart(*args) for args in attachments]
mime_blob = MIMEMultipart(_subparts=subparts)
return mime_blob.as_string()
def delete_server(self, server):
'''
Deletes a server and waits for it to disappear from Nova.
'''
if not server:
return
try:
server.delete()
except Exception as exc:
self.ignore_not_found(exc)
return
while True:
yield
try:
self.refresh_server(server)
except Exception as exc:
self.ignore_not_found(exc)
break
else:
# Some clouds append extra (STATUS) strings to the status
short_server_status = server.status.split('(')[0]
if short_server_status == "DELETED":
break
if short_server_status == "ERROR":
fault = getattr(server, 'fault', {})
message = fault.get('message', 'Unknown')
code = fault.get('code')
errmsg = (_("Server %(name)s delete failed: (%(code)s) "
"%(message)s"))
raise exception.Error(errmsg % {"name": server.name,
"code": code,
"message": message})
@scheduler.wrappertask
def resize(self, server, flavor, flavor_id):
"""Resize the server and then call check_resize task to verify."""
server.resize(flavor_id)
yield self.check_resize(server, flavor, flavor_id)
def rename(self, server, name):
"""Update the name for a server."""
server.update(name)
def check_resize(self, server, flavor, flavor_id):
"""
Verify that a resizing server is properly resized.
If that's the case, confirm the resize, if not raise an error.
"""
self.refresh_server(server)
while server.status == 'RESIZE':
yield
self.refresh_server(server)
if server.status == 'VERIFY_RESIZE':
server.confirm_resize()
else:
raise exception.Error(
_("Resizing to '%(flavor)s' failed, status '%(status)s'") %
dict(flavor=flavor, status=server.status))
@scheduler.wrappertask
def rebuild(self, server, image_id, preserve_ephemeral=False):
"""Rebuild the server and call check_rebuild to verify."""
server.rebuild(image_id, preserve_ephemeral=preserve_ephemeral)
yield self.check_rebuild(server, image_id)
def check_rebuild(self, server, image_id):
"""
Verify that a rebuilding server is rebuilt.
Raise error if it ends up in an ERROR state.
"""
self.refresh_server(server)
while server.status == 'REBUILD':
yield
self.refresh_server(server)
if server.status == 'ERROR':
raise exception.Error(
_("Rebuilding server failed, status '%s'") % server.status)
def meta_serialize(self, metadata):
"""
Serialize non-string metadata values before sending them to
Nova.
"""
return dict((key, (value if isinstance(value,
six.string_types)
else json.dumps(value))
) for (key, value) in metadata.items())
def meta_update(self, server, metadata):
"""Delete/Add the metadata in nova as needed."""
metadata = self.meta_serialize(metadata)
current_md = server.metadata
to_del = [key for key in current_md.keys() if key not in metadata]
client = self.client()
if len(to_del) > 0:
client.servers.delete_meta(server, to_del)
client.servers.set_meta(server, metadata)
def server_to_ipaddress(self, server):
'''
Return the server's IP address, fetching it from Nova.
'''
try:
server = self.client().servers.get(server)
except exceptions.NotFound as ex:
LOG.warn(_('Instance (%(server)s) not found: %(ex)s')
% {'server': server, 'ex': ex})
else:
for n in server.networks:
if len(server.networks[n]) > 0:
return server.networks[n][0]
def absolute_limits(self):
"""Return the absolute limits as a dictionary."""
limits = self.client().limits.get()
return dict([(limit.name, limit.value)
for limit in list(limits.absolute)])

View File

@ -14,6 +14,7 @@
from troveclient import client as tc
from troveclient.openstack.common.apiclient import exceptions
from heat.common import exception
from heat.engine.clients import client_plugin
@ -49,3 +50,25 @@ class TroveClientPlugin(client_plugin.ClientPlugin):
def is_over_limit(self, ex):
return isinstance(ex, exceptions.RequestEntityTooLarge)
def get_flavor_id(self, flavor):
'''
Get the id for the specified flavor name.
If the specified value is flavor id, just return it.
:param flavor: the name of the flavor to find
:returns: the id of :flavor:
:raises: exception.FlavorMissing
'''
flavor_id = None
flavor_list = self.client().flavors.list()
for o in flavor_list:
if o.name == flavor:
flavor_id = o.id
break
if o.id == flavor:
flavor_id = o.id
break
if flavor_id is None:
raise exception.FlavorMissing(flavor_id=flavor)
return flavor_id

View File

@ -23,7 +23,6 @@ from heat.engine import properties
from heat.engine import resource
from heat.engine.resources.network_interface import NetworkInterface
from heat.engine.resources.neutron import neutron
from heat.engine.resources import nova_utils
from heat.engine.resources import volume
from heat.engine import scheduler
from heat.engine import signal_responder
@ -432,8 +431,8 @@ class Instance(resource.Resource):
Return the server's IP address, fetching it from Nova if necessary
'''
if self.ipaddress is None:
self.ipaddress = nova_utils.server_to_ipaddress(
self.nova(), self.resource_id)
self.ipaddress = self.client_plugin().server_to_ipaddress(
self.resource_id)
return self.ipaddress or '0.0.0.0'
@ -547,7 +546,7 @@ class Instance(resource.Resource):
image_id = self.client_plugin('glance').get_image_id(image_name)
flavor_id = nova_utils.get_flavor_id(self.nova(), flavor)
flavor_id = self.client_plugin().get_flavor_id(flavor)
scheduler_hints = {}
if self.properties[self.NOVA_SCHEDULER_HINTS]:
@ -587,8 +586,8 @@ class Instance(resource.Resource):
flavor=flavor_id,
key_name=self.properties[self.KEY_NAME],
security_groups=security_groups,
userdata=nova_utils.build_userdata(self, userdata,
instance_user),
userdata=self.client_plugin().build_userdata(
self.metadata_get(), userdata, instance_user),
meta=self._get_nova_metadata(self.properties),
scheduler_hints=scheduler_hints,
nics=nics,
@ -624,15 +623,16 @@ class Instance(resource.Resource):
return volume_attach_task.step()
def _check_active(self, server):
cp = self.client_plugin()
if server.status != 'ACTIVE':
nova_utils.refresh_server(server)
cp.refresh_server(server)
if server.status == 'ACTIVE':
return True
# Some clouds append extra (STATUS) strings to the status
short_server_status = server.status.split('(')[0]
if short_server_status in nova_utils.deferred_server_statuses:
if short_server_status in cp.deferred_server_statuses:
return False
if server.status == 'ERROR':
@ -671,17 +671,16 @@ class Instance(resource.Resource):
server = None
if self.TAGS in prop_diff:
server = self.nova().servers.get(self.resource_id)
nova_utils.meta_update(self.nova(),
server,
self._get_nova_metadata(prop_diff))
self.client_plugin().meta_update(
server, self._get_nova_metadata(prop_diff))
if self.INSTANCE_TYPE in prop_diff:
flavor = prop_diff[self.INSTANCE_TYPE]
flavor_id = nova_utils.get_flavor_id(self.nova(), flavor)
flavor_id = self.client_plugin().get_flavor_id(flavor)
if not server:
server = self.nova().servers.get(self.resource_id)
checker = scheduler.TaskRunner(nova_utils.resize, server, flavor,
flavor_id)
checker = scheduler.TaskRunner(self.client_plugin().resize,
server, flavor, flavor_id)
checkers.append(checker)
if self.NETWORK_INTERFACES in prop_diff:
new_network_ifaces = prop_diff.get(self.NETWORK_INTERFACES)
@ -815,11 +814,11 @@ class Instance(resource.Resource):
server = self.nova().servers.get(self.resource_id)
except Exception as e:
self.client_plugin().ignore_not_found(e)
self.resource_id_set(None)
return
deleters = (
scheduler.TaskRunner(self._detach_volumes_task()),
scheduler.TaskRunner(nova_utils.delete_server, server))
scheduler.TaskRunner(self.client_plugin().delete_server,
server))
deleters[0].start()
return deleters
@ -872,11 +871,12 @@ class Instance(resource.Resource):
if server.status == 'SUSPENDED':
return True
nova_utils.refresh_server(server)
cp = self.client_plugin()
cp.refresh_server(server)
LOG.debug("%(name)s check_suspend_complete "
"status = %(status)s",
{'name': self.name, 'status': server.status})
if server.status in list(nova_utils.deferred_server_statuses +
if server.status in list(cp.deferred_server_statuses +
['ACTIVE']):
return server.status == 'SUSPENDED'
else:

View File

@ -19,7 +19,6 @@ from heat.common import template_format
from heat.engine import attributes
from heat.engine import constraints
from heat.engine import properties
from heat.engine.resources import nova_utils
from heat.engine import stack_resource
from heat.openstack.common.gettextutils import _
from heat.openstack.common import log as logging
@ -422,9 +421,9 @@ class LoadBalancer(stack_resource.StackResource):
servers = []
n = 1
client = self.nova()
nova_cp = self.client_plugin('nova')
for i in instances:
ip = nova_utils.server_to_ipaddress(client, i) or '0.0.0.0'
ip = nova_cp.server_to_ipaddress(i) or '0.0.0.0'
LOG.debug('haproxy server:%s' % ip)
servers.append('%sserver server%d %s:%s %s' % (spaces, n,
ip, inst_port,

View File

@ -18,7 +18,6 @@ from heat.engine import properties
from heat.engine import resource
from heat.engine.resources.neutron import neutron
from heat.engine.resources.neutron import neutron_utils
from heat.engine.resources import nova_utils
from heat.engine import scheduler
from heat.engine import support
@ -647,11 +646,10 @@ class LoadBalancer(resource.Resource):
def handle_create(self):
pool = self.properties[self.POOL_ID]
client = self.neutron()
nova_client = self.nova()
protocol_port = self.properties[self.PROTOCOL_PORT]
for member in self.properties.get(self.MEMBERS):
address = nova_utils.server_to_ipaddress(nova_client, member)
address = self.client_plugin('nova').server_to_ipaddress(member)
lb_member = client.create_member({
'member': {
'pool_id': pool,
@ -673,10 +671,10 @@ class LoadBalancer(resource.Resource):
self.client_plugin().ignore_not_found(ex)
self.data_delete(member)
pool = self.properties[self.POOL_ID]
nova_client = self.nova()
protocol_port = self.properties[self.PROTOCOL_PORT]
for member in members - old_members:
address = nova_utils.server_to_ipaddress(nova_client, member)
address = self.client_plugin('nova').server_to_ipaddress(
member)
lb_member = client.create_member({
'member': {
'pool_id': pool,

View File

@ -16,7 +16,6 @@ from heat.engine import attributes
from heat.engine import constraints
from heat.engine import properties
from heat.engine import resource
from heat.engine.resources import nova_utils
from heat.openstack.common.gettextutils import _
@ -97,8 +96,7 @@ class KeyPair(resource.Resource):
if self.properties[self.PUBLIC_KEY]:
self._public_key = self.properties[self.PUBLIC_KEY]
elif self.resource_id:
nova_key = nova_utils.get_keypair(self.nova(),
self.resource_id)
nova_key = self.client_plugin().get_keypair(self.resource_id)
self._public_key = nova_key.public_key
return self._public_key
@ -138,8 +136,7 @@ class KeypairConstraint(constraints.BaseCustomConstraint):
# Don't validate empty key, which can happen when you use a KeyPair
# resource
return True
nova_client = client.client('nova')
nova_utils.get_keypair(nova_client, value)
client.client_plugin('nova').get_keypair(value)
def resource_mapping():

View File

@ -24,6 +24,7 @@ import string
from oslo.config import cfg
import six
from six.moves.urllib import parse as urlparse
import warnings
from heat.common import exception
from heat.engine import scheduler
@ -49,6 +50,8 @@ def refresh_server(server):
'''
Refresh server's attributes and log warnings for non-critical API errors.
'''
warnings.warn('nova_utils.refresh_server is deprecated. '
'Use self.client_plugin("nova").refresh_server')
try:
server.get()
except nova_exceptions.OverLimit as exc:
@ -72,6 +75,8 @@ def refresh_server(server):
def get_ip(server, net_type, ip_version):
"""Return the server's IP of the given type and version."""
warnings.warn('nova_utils.get_ip is deprecated. '
'Use self.client_plugin("nova").get_ip')
if net_type in server.addresses:
for ip in server.addresses[net_type]:
if ip['version'] == ip_version:
@ -79,6 +84,8 @@ def get_ip(server, net_type, ip_version):
def get_flavor_id(nova_client, flavor):
warnings.warn('nova_utils.get_flavor_id is deprecated. '
'Use self.client_plugin("nova").get_flavor_id')
'''
Get the id for the specified flavor name.
If the specified value is flavor id, just return it.
@ -103,6 +110,8 @@ def get_flavor_id(nova_client, flavor):
def get_keypair(nova_client, key_name):
warnings.warn('nova_utils.get_keypair is deprecated. '
'Use self.client_plugin("nova").get_keypair')
'''
Get the public key specified by :key_name:
@ -119,6 +128,8 @@ def get_keypair(nova_client, key_name):
def build_userdata(resource, userdata=None, instance_user=None,
user_data_format='HEAT_CFNTOOLS'):
warnings.warn('nova_utils.build_userdata is deprecated. '
'Use self.client_plugin("nova").build_userdata')
'''
Build multipart data blob for CloudInit which includes user-supplied
Metadata, user data, and the required Heat in-instance configuration.
@ -241,6 +252,8 @@ def delete_server(server):
A co-routine that deletes the server and waits for it to
disappear from Nova.
'''
warnings.warn('nova_utils.delete_server is deprecated. '
'Use self.client_plugin("nova").delete_server')
if not server:
return
try:
@ -274,12 +287,16 @@ def delete_server(server):
@scheduler.wrappertask
def resize(server, flavor, flavor_id):
"""Resize the server and then call check_resize task to verify."""
warnings.warn('nova_utils.resize is deprecated. '
'Use self.client_plugin("nova").resize')
server.resize(flavor_id)
yield check_resize(server, flavor, flavor_id)
def rename(server, name):
"""Update the name for a server."""
warnings.warn('nova_utils.rename is deprecated. '
'Use self.client_plugin("nova").rename')
server.update(name)
@ -288,6 +305,8 @@ def check_resize(server, flavor, flavor_id):
Verify that a resizing server is properly resized.
If that's the case, confirm the resize, if not raise an error.
"""
warnings.warn('nova_utils.check_resize is deprecated. '
'Use self.client_plugin("nova").check_resize')
refresh_server(server)
while server.status == 'RESIZE':
yield
@ -303,6 +322,8 @@ def check_resize(server, flavor, flavor_id):
@scheduler.wrappertask
def rebuild(server, image_id, preserve_ephemeral=False):
"""Rebuild the server and call check_rebuild to verify."""
warnings.warn('nova_utils.rebuild is deprecated. '
'Use self.client_plugin("nova").rebuild')
server.rebuild(image_id, preserve_ephemeral=preserve_ephemeral)
yield check_rebuild(server, image_id)
@ -312,6 +333,8 @@ def check_rebuild(server, image_id):
Verify that a rebuilding server is rebuilt.
Raise error if it ends up in an ERROR state.
"""
warnings.warn('nova_utils.check_rebuild is deprecated. '
'Use self.client_plugin("nova").check_rebuild')
refresh_server(server)
while server.status == 'REBUILD':
yield
@ -326,6 +349,8 @@ def meta_serialize(metadata):
Serialize non-string metadata values before sending them to
Nova.
"""
warnings.warn('nova_utils.meta_serialize is deprecated. '
'Use self.client_plugin("nova").meta_serialize')
return dict((key, (value if isinstance(value,
six.string_types)
else json.dumps(value))
@ -334,6 +359,8 @@ def meta_serialize(metadata):
def meta_update(client, server, metadata):
"""Delete/Add the metadata in nova as needed."""
warnings.warn('nova_utils.meta_update is deprecated. '
'Use self.client_plugin("nova").meta_update')
metadata = meta_serialize(metadata)
current_md = server.metadata
to_del = [key for key in current_md.keys() if key not in metadata]
@ -347,6 +374,8 @@ def server_to_ipaddress(client, server):
'''
Return the server's IP address, fetching it from Nova.
'''
warnings.warn('nova_utils.server_to_ipaddress is deprecated. '
'Use self.client_plugin("nova").server_to_ipaddress')
try:
server = client.servers.get(server)
except nova_exceptions.NotFound as ex:
@ -360,5 +389,7 @@ def server_to_ipaddress(client, server):
def absolute_limits(nova_client):
"""Return the absolute limits as a dictionary."""
warnings.warn('nova_utils.absolute_limits is deprecated. '
'Use self.client_plugin("nova").absolute_limits')
limits = nova_client.limits.get()
return dict([(limit.name, limit.value) for limit in list(limits.absolute)])

View File

@ -16,7 +16,6 @@ from heat.engine import attributes
from heat.engine import constraints
from heat.engine import properties
from heat.engine import resource
from heat.engine.resources import nova_utils
from heat.openstack.common.gettextutils import _
from heat.openstack.common import log as logging
@ -228,7 +227,7 @@ class OSDBInstance(resource.Resource):
'''
Create cloud database instance.
'''
self.flavor = nova_utils.get_flavor_id(self.trove(),
self.flavor = self.client_plugin().get_flavor_id(
self.properties[self.FLAVOR])
self.volume = {'size': self.properties[self.SIZE]}
self.databases = self.properties.get(self.DATABASES)

View File

@ -22,7 +22,6 @@ from heat.engine import constraints
from heat.engine import properties
from heat.engine import resource
from heat.engine.resources.neutron import subnet
from heat.engine.resources import nova_utils
from heat.engine import scheduler
from heat.engine import stack_user
from heat.engine import support
@ -471,8 +470,8 @@ class Server(stack_user.StackUser):
else:
instance_user = None
userdata = nova_utils.build_userdata(
self,
userdata = self.client_plugin().build_userdata(
self.metadata_get(),
ud_content,
instance_user=instance_user,
user_data_format=user_data_format)
@ -484,11 +483,12 @@ class Server(stack_user.StackUser):
if image:
image = self.client_plugin('glance').get_image_id(image)
flavor_id = nova_utils.get_flavor_id(self.nova(), flavor)
flavor_id = self.client_plugin().get_flavor_id(flavor)
instance_meta = self.properties.get(self.METADATA)
if instance_meta is not None:
instance_meta = nova_utils.meta_serialize(instance_meta)
instance_meta = self.client_plugin().meta_serialize(
instance_meta)
scheduler_hints = self.properties.get(self.SCHEDULER_HINTS)
nics = self._build_nics(self.properties.get(self.NETWORKS))
@ -532,12 +532,13 @@ class Server(stack_user.StackUser):
def _check_active(self, server):
cp = self.client_plugin()
if server.status != 'ACTIVE':
nova_utils.refresh_server(server)
cp.refresh_server(server)
# Some clouds append extra (STATUS) strings to the status
short_server_status = server.status.split('(')[0]
if short_server_status in nova_utils.deferred_server_statuses:
if short_server_status in cp.deferred_server_statuses:
return False
elif server.status == 'ACTIVE':
return True
@ -621,8 +622,8 @@ class Server(stack_user.StackUser):
def _resolve_attribute(self, name):
if name == self.FIRST_ADDRESS:
return nova_utils.server_to_ipaddress(
self.nova(), self.resource_id) or ''
return self.client_plugin().server_to_ipaddress(
self.resource_id) or ''
try:
server = self.nova().servers.get(self.resource_id)
except Exception as e:
@ -718,8 +719,7 @@ class Server(stack_user.StackUser):
if self.METADATA in prop_diff:
server = self.nova().servers.get(self.resource_id)
nova_utils.meta_update(self.nova(),
server,
self.client_plugin().meta_update(server,
prop_diff[self.METADATA])
if self.FLAVOR in prop_diff:
@ -732,11 +732,11 @@ class Server(stack_user.StackUser):
raise resource.UpdateReplace(self.name)
flavor = prop_diff[self.FLAVOR]
flavor_id = nova_utils.get_flavor_id(self.nova(), flavor)
flavor_id = self.client_plugin().get_flavor_id(flavor)
if not server:
server = self.nova().servers.get(self.resource_id)
checker = scheduler.TaskRunner(nova_utils.resize, server, flavor,
flavor_id)
checker = scheduler.TaskRunner(self.client_plugin().resize,
server, flavor, flavor_id)
checkers.append(checker)
if self.IMAGE in prop_diff:
@ -752,14 +752,14 @@ class Server(stack_user.StackUser):
preserve_ephemeral = (
image_update_policy == 'REBUILD_PRESERVE_EPHEMERAL')
checker = scheduler.TaskRunner(
nova_utils.rebuild, server, image_id,
self.client_plugin().rebuild, server, image_id,
preserve_ephemeral=preserve_ephemeral)
checkers.append(checker)
if self.NAME in prop_diff:
if not server:
server = self.nova().servers.get(self.resource_id)
nova_utils.rename(server, prop_diff[self.NAME])
self.client_plugin().rename(server, prop_diff[self.NAME])
if self.NETWORKS in prop_diff:
new_networks = prop_diff.get(self.NETWORKS)
@ -928,7 +928,7 @@ class Server(stack_user.StackUser):
metadata = self.properties.get(self.METADATA)
personality = self.properties.get(self.PERSONALITY)
if metadata is not None or personality:
limits = nova_utils.absolute_limits(self.nova())
limits = self.client_plugin().absolute_limits()
# if 'security_groups' present for the server and explict 'port'
# in one or more entries in 'networks', raise validation error
@ -977,7 +977,8 @@ class Server(stack_user.StackUser):
except Exception as e:
self.client_plugin().ignore_not_found(e)
else:
deleter = scheduler.TaskRunner(nova_utils.delete_server, server)
deleter = scheduler.TaskRunner(self.client_plugin().delete_server,
server)
deleter.start()
return deleter
@ -1022,10 +1023,11 @@ class Server(stack_user.StackUser):
if server.status == 'SUSPENDED':
return True
nova_utils.refresh_server(server)
cp = self.client_plugin()
cp.refresh_server(server)
LOG.debug('%(name)s check_suspend_complete status = %(status)s'
% {'name': self.name, 'status': server.status})
if server.status in list(nova_utils.deferred_server_statuses +
if server.status in list(cp.deferred_server_statuses +
['ACTIVE']):
return server.status == 'SUSPENDED'
else:
@ -1067,8 +1069,7 @@ class FlavorConstraint(constraints.BaseCustomConstraint):
expected_exceptions = (exception.FlavorMissing,)
def validate_with_client(self, client, value):
nova_client = client.client('nova')
nova_utils.get_flavor_id(nova_client, value)
client.client_plugin('nova').get_flavor_id(value)
def resource_mapping():

View File

@ -40,7 +40,6 @@ from heat.engine import parser
from heat.engine.properties import Properties
from heat.engine import resource as res
from heat.engine.resources import instance as instances
from heat.engine.resources import nova_utils
from heat.engine import service
from heat.engine import stack_lock
from heat.engine import watchrule
@ -209,6 +208,7 @@ def setup_mocks(mocks, stack, mock_image_constraint=True):
mocks.StubOutWithMock(nova.NovaClientPlugin, '_create')
nova.NovaClientPlugin._create().AndReturn(fc)
instance = stack['WebServer']
metadata = instance.metadata_get()
if mock_image_constraint:
setup_mock_for_image_constraint(mocks,
instance.t['Properties']['ImageId'])
@ -216,11 +216,11 @@ def setup_mocks(mocks, stack, mock_image_constraint=True):
setup_keystone_mocks(mocks, stack)
user_data = instance.properties['UserData']
server_userdata = nova_utils.build_userdata(instance, user_data,
'ec2-user')
mocks.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(
instance,
server_userdata = instance.client_plugin().build_userdata(
metadata, user_data, 'ec2-user')
mocks.StubOutWithMock(nova.NovaClientPlugin, 'build_userdata')
nova.NovaClientPlugin.build_userdata(
metadata,
instance.t['Properties']['UserData'],
'ec2-user').AndReturn(server_userdata)

View File

@ -28,7 +28,6 @@ from heat.engine import parser
from heat.engine import resource
from heat.engine.resources import instance as instances
from heat.engine.resources import network_interface
from heat.engine.resources import nova_utils
from heat.engine import scheduler
from heat.tests.common import HeatTestCase
from heat.tests import utils
@ -1336,8 +1335,10 @@ class InstancesTest(HeatTestCase):
"""The default value for instance_user in heat.conf is ec2-user."""
return_server = self.fc.servers.list()[1]
instance = self._setup_test_instance(return_server, 'default_user')
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(instance, 'wordpress', 'ec2-user')
metadata = instance.metadata_get()
self.m.StubOutWithMock(nova.NovaClientPlugin, 'build_userdata')
nova.NovaClientPlugin.build_userdata(
metadata, 'wordpress', 'ec2-user')
self.m.ReplayAll()
scheduler.TaskRunner(instance.create)()
self.m.VerifyAll()
@ -1354,8 +1355,10 @@ class InstancesTest(HeatTestCase):
instance = self._setup_test_instance(return_server, 'custom_user')
self.m.StubOutWithMock(instances.cfg.CONF, 'instance_user')
instances.cfg.CONF.instance_user = 'custom_user'
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(instance, 'wordpress', 'custom_user')
metadata = instance.metadata_get()
self.m.StubOutWithMock(nova.NovaClientPlugin, 'build_userdata')
nova.NovaClientPlugin.build_userdata(
metadata, 'wordpress', 'custom_user')
self.m.ReplayAll()
scheduler.TaskRunner(instance.create)()
self.m.VerifyAll()
@ -1373,8 +1376,10 @@ class InstancesTest(HeatTestCase):
instance = self._setup_test_instance(return_server, 'empty_user')
self.m.StubOutWithMock(instances.cfg.CONF, 'instance_user')
instances.cfg.CONF.instance_user = ''
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(instance, 'wordpress', 'ec2-user')
metadata = instance.metadata_get()
self.m.StubOutWithMock(nova.NovaClientPlugin, 'build_userdata')
nova.NovaClientPlugin.build_userdata(
metadata, 'wordpress', 'ec2-user')
self.m.ReplayAll()
scheduler.TaskRunner(instance.create)()
self.m.VerifyAll()

View File

@ -20,7 +20,6 @@ from heat.engine import environment
from heat.engine import parser
from heat.engine.resources import instance as instances
from heat.engine.resources import network_interface as network_interfaces
from heat.engine.resources import nova_utils
from heat.engine import scheduler
from heat.tests.common import HeatTestCase
from heat.tests import utils
@ -170,6 +169,7 @@ class instancesTest(HeatTestCase):
resource_defns = stack.t.resource_definitions(stack)
instance = instances.Instance('%s_name' % name,
resource_defns['WebServer'], stack)
metadata = instance.metadata_get()
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
nova.NovaClientPlugin._create().AndReturn(self.fc)
@ -180,13 +180,13 @@ class instancesTest(HeatTestCase):
instance.neutron().MultipleTimes().AndReturn(FakeNeutron())
# need to resolve the template functions
server_userdata = nova_utils.build_userdata(
instance,
server_userdata = instance.client_plugin().build_userdata(
metadata,
instance.t['Properties']['UserData'],
'ec2-user')
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(
instance,
self.m.StubOutWithMock(nova.NovaClientPlugin, 'build_userdata')
nova.NovaClientPlugin.build_userdata(
metadata,
instance.t['Properties']['UserData'],
'ec2-user').AndReturn(server_userdata)
@ -225,6 +225,7 @@ class instancesTest(HeatTestCase):
instance = instances.Instance('%s_name' % name,
resource_defns['WebServer'], stack)
metadata = instance.metadata_get()
self._mock_get_image_id_success(image_id, 1)
self.m.StubOutWithMock(nic, 'neutron')
@ -234,13 +235,13 @@ class instancesTest(HeatTestCase):
nova.NovaClientPlugin._create().AndReturn(self.fc)
# need to resolve the template functions
server_userdata = nova_utils.build_userdata(
instance,
server_userdata = instance.client_plugin().build_userdata(
metadata,
instance.t['Properties']['UserData'],
'ec2-user')
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(
instance,
self.m.StubOutWithMock(nova.NovaClientPlugin, 'build_userdata')
nova.NovaClientPlugin.build_userdata(
metadata,
instance.t['Properties']['UserData'],
'ec2-user').AndReturn(server_userdata)

View File

@ -20,10 +20,11 @@ from oslo.config import cfg
from heat.common import template_format
from heat.db import api as db_api
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.resources import instance
from heat.engine.resources import nova_utils
from heat.engine import template
from heat.tests.common import HeatTestCase
from heat.tests import utils
@ -123,11 +124,12 @@ class AutoScalingTest(HeatTestCase):
self.m.StubOutWithMock(neutronclient.Client, 'create_member')
self.m.StubOutWithMock(neutronclient.Client, 'list_members')
self.m.StubOutWithMock(nova_utils, 'server_to_ipaddress')
self.m.StubOutWithMock(nova.NovaClientPlugin, 'server_to_ipaddress')
self.m.StubOutWithMock(parser.Stack, 'validate')
self.m.StubOutWithMock(instance.Instance, 'handle_create')
self.m.StubOutWithMock(instance.Instance, 'check_create_complete')
self.m.StubOutWithMock(glance.ImageConstraint, "validate")
def test_lb(self):
@ -276,10 +278,10 @@ class AutoScalingTest(HeatTestCase):
instance.Instance.check_create_complete(mox.IgnoreArg())\
.AndReturn(True)
self.stub_ImageConstraint_validate()
glance.ImageConstraint.validate(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(True)
nova_utils.server_to_ipaddress(
mox.IgnoreArg(),
nova.NovaClientPlugin.server_to_ipaddress(
mox.IgnoreArg()).AndReturn('1.2.3.4')
neutronclient.Client.create_member(membera_block).\
@ -304,15 +306,13 @@ class AutoScalingTest(HeatTestCase):
instance.Instance.check_create_complete(mox.IgnoreArg())\
.AndReturn(True)
nova_utils.server_to_ipaddress(
mox.IgnoreArg(),
nova.NovaClientPlugin.server_to_ipaddress(
mox.IgnoreArg()).AndReturn('1.2.3.5')
neutronclient.Client.create_member(memberb_block).\
AndReturn(memberb_ret_block)
nova_utils.server_to_ipaddress(
mox.IgnoreArg(),
nova.NovaClientPlugin.server_to_ipaddress(
mox.IgnoreArg()).AndReturn('1.2.3.6')
neutronclient.Client.create_member(memberc_block).\

View File

@ -15,7 +15,6 @@ from heat.common import template_format
from heat.engine.clients.os import glance
from heat.engine.clients.os import nova
from heat.engine.resources import instance as instances
from heat.engine.resources import nova_utils
from heat.engine import scheduler
from heat.tests.common import HeatTestCase
from heat.tests import utils
@ -66,13 +65,14 @@ class nokeyTest(HeatTestCase):
'CentOS 5.2').MultipleTimes().AndReturn(1)
# need to resolve the template functions
server_userdata = nova_utils.build_userdata(
instance,
metadata = instance.metadata_get()
server_userdata = instance.client_plugin().build_userdata(
metadata,
instance.t['Properties']['UserData'],
'ec2-user')
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(
instance,
self.m.StubOutWithMock(nova.NovaClientPlugin, 'build_userdata')
nova.NovaClientPlugin.build_userdata(
metadata,
instance.t['Properties']['UserData'],
'ec2-user').AndReturn(server_userdata)

View File

@ -0,0 +1,238 @@
#
# 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.
"""Tests for :module:'heat.engine.resources.nova_utls'."""
import mock
from novaclient import exceptions as nova_exceptions
from oslo.config import cfg
import uuid
from heat.common import exception
from heat.engine.clients.os import nova
from heat.tests.common import HeatTestCase
from heat.tests import utils
class NovaClientPluginTestCase(HeatTestCase):
def setUp(self):
super(NovaClientPluginTestCase, self).setUp()
self.nova_client = self.m.CreateMockAnything()
con = utils.dummy_context()
c = con.clients
self.nova_plugin = c.client_plugin('nova')
self.nova_plugin._client = self.nova_client
class NovaClientPluginTests(NovaClientPluginTestCase):
"""
Basic tests for the helper methods in
:module:'heat.engine.resources.nova_utils'.
"""
def test_get_ip(self):
my_image = self.m.CreateMockAnything()
my_image.addresses = {
'public': [{'version': 4,
'addr': '4.5.6.7'},
{'version': 6,
'addr': '2401:1801:7800:0101:c058:dd33:ff18:04e6'}],
'private': [{'version': 4,
'addr': '10.13.12.13'}]}
expected = '4.5.6.7'
observed = self.nova_plugin.get_ip(my_image, 'public', 4)
self.assertEqual(expected, observed)
expected = '10.13.12.13'
observed = self.nova_plugin.get_ip(my_image, 'private', 4)
self.assertEqual(expected, observed)
expected = '2401:1801:7800:0101:c058:dd33:ff18:04e6'
observed = self.nova_plugin.get_ip(my_image, 'public', 6)
self.assertEqual(expected, observed)
def test_get_flavor_id(self):
"""Tests the get_flavor_id function."""
flav_id = str(uuid.uuid4())
flav_name = 'X-Large'
my_flavor = self.m.CreateMockAnything()
my_flavor.name = flav_name
my_flavor.id = flav_id
self.nova_client.flavors = self.m.CreateMockAnything()
self.nova_client.flavors.list().MultipleTimes().AndReturn([my_flavor])
self.m.ReplayAll()
self.assertEqual(flav_id, self.nova_plugin.get_flavor_id(flav_name))
self.assertEqual(flav_id, self.nova_plugin.get_flavor_id(flav_id))
self.assertRaises(exception.FlavorMissing,
self.nova_plugin.get_flavor_id, 'noflavor')
self.m.VerifyAll()
def test_get_keypair(self):
"""Tests the get_keypair function."""
my_pub_key = 'a cool public key string'
my_key_name = 'mykey'
my_key = self.m.CreateMockAnything()
my_key.public_key = my_pub_key
my_key.name = my_key_name
self.nova_client.keypairs = self.m.CreateMockAnything()
self.nova_client.keypairs.get(
my_key_name).AndReturn(my_key)
self.nova_client.keypairs.get(
'notakey').AndRaise(nova_exceptions.NotFound(404))
self.m.ReplayAll()
self.assertEqual(my_key, self.nova_plugin.get_keypair(my_key_name))
self.assertRaises(exception.UserKeyPairMissing,
self.nova_plugin.get_keypair, 'notakey')
self.m.VerifyAll()
class NovaUtilsRefreshServerTests(NovaClientPluginTestCase):
def test_successful_refresh(self):
server = self.m.CreateMockAnything()
server.get().AndReturn(None)
self.m.ReplayAll()
self.assertIsNone(self.nova_plugin.refresh_server(server))
self.m.VerifyAll()
def test_overlimit_error(self):
server = mock.Mock()
server.get.side_effect = nova_exceptions.OverLimit(
413, "limit reached")
self.assertIsNone(self.nova_plugin.refresh_server(server))
def test_500_error(self):
server = self.m.CreateMockAnything()
msg = ("ClientException: The server has either erred or is "
"incapable of performing the requested operation.")
server.get().AndRaise(
nova_exceptions.ClientException(500, msg))
self.m.ReplayAll()
self.assertIsNone(self.nova_plugin.refresh_server(server))
self.m.VerifyAll()
def test_503_error(self):
server = self.m.CreateMockAnything()
msg = ("ClientException: The server has either erred or is "
"incapable of performing the requested operation.")
server.get().AndRaise(
nova_exceptions.ClientException(503, msg))
self.m.ReplayAll()
self.assertIsNone(self.nova_plugin.refresh_server(server))
self.m.VerifyAll()
def test_unhandled_exception(self):
server = self.m.CreateMockAnything()
msg = ("ClientException: The server has either erred or is "
"incapable of performing the requested operation.")
server.get().AndRaise(
nova_exceptions.ClientException(501, msg))
self.m.ReplayAll()
self.assertRaises(nova_exceptions.ClientException,
self.nova_plugin.refresh_server, server)
self.m.VerifyAll()
class NovaUtilsUserdataTests(NovaClientPluginTestCase):
def test_build_userdata(self):
"""Tests the build_userdata function."""
cfg.CONF.set_override('heat_metadata_server_url',
'http://server.test:123')
cfg.CONF.set_override('heat_watch_server_url',
'http://server.test:345')
cfg.CONF.set_override('instance_connection_is_secure',
False)
cfg.CONF.set_override(
'instance_connection_https_validate_certificates', False)
data = self.nova_plugin.build_userdata({})
self.assertIn("Content-Type: text/cloud-config;", data)
self.assertIn("Content-Type: text/cloud-boothook;", data)
self.assertIn("Content-Type: text/part-handler;", data)
self.assertIn("Content-Type: text/x-cfninitdata;", data)
self.assertIn("Content-Type: text/x-shellscript;", data)
self.assertIn("http://server.test:345", data)
self.assertIn("http://server.test:123", data)
self.assertIn("[Boto]", data)
def test_build_userdata_without_instance_user(self):
"""Don't add a custom instance user when not requested."""
cfg.CONF.set_override('instance_user',
'config_instance_user')
cfg.CONF.set_override('heat_metadata_server_url',
'http://server.test:123')
cfg.CONF.set_override('heat_watch_server_url',
'http://server.test:345')
data = self.nova_plugin.build_userdata({}, instance_user=None)
self.assertNotIn('user: ', data)
self.assertNotIn('useradd', data)
self.assertNotIn('config_instance_user', data)
def test_build_userdata_with_instance_user(self):
"""Add the custom instance user when requested."""
self.m.StubOutWithMock(nova.cfg, 'CONF')
cnf = nova.cfg.CONF
cnf.instance_user = 'config_instance_user'
cnf.heat_metadata_server_url = 'http://server.test:123'
cnf.heat_watch_server_url = 'http://server.test:345'
data = self.nova_plugin.build_userdata(
None, instance_user="custominstanceuser")
self.assertNotIn('config_instance_user', data)
self.assertIn("custominstanceuser", data)
class NovaUtilsMetadataTests(NovaClientPluginTestCase):
def test_serialize_string(self):
original = {'test_key': 'simple string value'}
self.assertEqual(original, self.nova_plugin.meta_serialize(original))
def test_serialize_int(self):
original = {'test_key': 123}
expected = {'test_key': '123'}
self.assertEqual(expected, self.nova_plugin.meta_serialize(original))
def test_serialize_list(self):
original = {'test_key': [1, 2, 3]}
expected = {'test_key': '[1, 2, 3]'}
self.assertEqual(expected, self.nova_plugin.meta_serialize(original))
def test_serialize_dict(self):
original = {'test_key': {'a': 'b', 'c': 'd'}}
expected = {'test_key': '{"a": "b", "c": "d"}'}
self.assertEqual(expected, self.nova_plugin.meta_serialize(original))
def test_serialize_none(self):
original = {'test_key': None}
expected = {'test_key': 'null'}
self.assertEqual(expected, self.nova_plugin.meta_serialize(original))
def test_serialize_combined(self):
original = {
'test_key_1': 123,
'test_key_2': 'a string',
'test_key_3': {'a': 'b'},
'test_key_4': None,
}
expected = {
'test_key_1': '123',
'test_key_2': 'a string',
'test_key_3': '{"a": "b"}',
'test_key_4': 'null',
}
self.assertEqual(expected, self.nova_plugin.meta_serialize(original))

View File

@ -35,6 +35,10 @@ class NovaUtilsTests(HeatTestCase):
def setUp(self):
super(NovaUtilsTests, self).setUp()
self.nova_client = self.m.CreateMockAnything()
self.mock_warnings = mock.patch(
'heat.engine.resources.nova_utils.warnings')
self.mock_warnings.start()
self.addCleanup(self.mock_warnings.stop)
def test_get_ip(self):
my_image = self.m.CreateMockAnything()
@ -126,6 +130,13 @@ class NovaUtilsTests(HeatTestCase):
class NovaUtilsRefreshServerTests(HeatTestCase):
def setUp(self):
super(NovaUtilsRefreshServerTests, self).setUp()
self.mock_warnings = mock.patch(
'heat.engine.resources.nova_utils.warnings')
self.mock_warnings.start()
self.addCleanup(self.mock_warnings.stop)
def test_successful_refresh(self):
server = self.m.CreateMockAnything()
server.get().AndReturn(None)
@ -170,6 +181,10 @@ class NovaUtilsUserdataTests(HeatTestCase):
def setUp(self):
super(NovaUtilsUserdataTests, self).setUp()
self.nova_client = self.m.CreateMockAnything()
self.mock_warnings = mock.patch(
'heat.engine.resources.nova_utils.warnings')
self.mock_warnings.start()
self.addCleanup(self.mock_warnings.stop)
def test_build_userdata(self):
"""Tests the build_userdata function."""
@ -228,6 +243,13 @@ class NovaUtilsUserdataTests(HeatTestCase):
class NovaUtilsMetadataTests(HeatTestCase):
def setUp(self):
super(NovaUtilsMetadataTests, self).setUp()
self.mock_warnings = mock.patch(
'heat.engine.resources.nova_utils.warnings')
self.mock_warnings.start()
self.addCleanup(self.mock_warnings.stop)
def test_serialize_string(self):
original = {'test_key': 'simple string value'}
self.assertEqual(original, nova_utils.meta_serialize(original))

View File

@ -18,6 +18,7 @@ import six
from heat.common import exception
from heat.common import template_format
from heat.engine.clients.os import trove
from heat.engine import parser
from heat.engine.resources import os_database
from heat.engine import scheduler
@ -97,12 +98,10 @@ class OSDBInstanceTest(HeatTestCase):
return instance
def _stubout_create(self, instance, fake_dbinstance):
self.m.StubOutWithMock(instance, 'trove')
instance.trove().MultipleTimes().AndReturn(self.fc)
self.m.StubOutWithMock(self.fc, 'flavors')
self.m.StubOutWithMock(self.fc.flavors, "list")
self.fc.flavors.list().AndReturn([FakeFlavor(1, '1GB'),
FakeFlavor(2, '2GB')])
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
trove.TroveClientPlugin._create().AndReturn(self.fc)
self.m.StubOutWithMock(trove.TroveClientPlugin, 'get_flavor_id')
trove.TroveClientPlugin.get_flavor_id('1GB').AndReturn(1)
self.m.StubOutWithMock(self.fc, 'instances')
self.m.StubOutWithMock(self.fc.instances, 'create')
users = [{"name": "testuser", "password": "pass", "host": "%",
@ -121,8 +120,8 @@ class OSDBInstanceTest(HeatTestCase):
self.m.ReplayAll()
def _stubout_validate(self, instance):
self.m.StubOutWithMock(instance, 'trove')
instance.trove().MultipleTimes().AndReturn(self.fc)
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
trove.TroveClientPlugin._create().AndReturn(self.fc)
self.m.StubOutWithMock(self.fc, 'datastore_versions')
self.m.StubOutWithMock(self.fc.datastore_versions, 'list')
self.fc.datastore_versions.list(instance.properties['datastore_type']
@ -144,8 +143,8 @@ class OSDBInstanceTest(HeatTestCase):
t['Resources']['MySqlCloudDB']['Properties']['restore_point'] = "1234"
instance = self._setup_test_clouddbinstance('dbinstance_create', t)
self.m.StubOutWithMock(instance, 'trove')
instance.trove().MultipleTimes().AndReturn(self.fc)
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
trove.TroveClientPlugin._create().AndReturn(self.fc)
self.m.StubOutWithMock(self.fc, 'flavors')
self.m.StubOutWithMock(self.fc.flavors, "list")
self.fc.flavors.list().AndReturn([FakeFlavor(1, '1GB'),
@ -281,8 +280,8 @@ class OSDBInstanceTest(HeatTestCase):
t = template_format.parse(db_template)
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
instance.resource_id = 12345
self.m.StubOutWithMock(instance, 'trove')
instance.trove().MultipleTimes().AndReturn(self.fc)
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
trove.TroveClientPlugin._create().AndReturn(self.fc)
self.m.StubOutWithMock(self.fc, 'instances')
self.m.StubOutWithMock(self.fc.instances, 'get')
self.fc.instances.get(12345).AndReturn(fake_dbinstance)
@ -296,8 +295,8 @@ class OSDBInstanceTest(HeatTestCase):
t = template_format.parse(db_template)
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
instance.resource_id = 12345
self.m.StubOutWithMock(instance, 'trove')
instance.trove().MultipleTimes().AndReturn(self.fc)
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
trove.TroveClientPlugin._create().AndReturn(self.fc)
self.m.StubOutWithMock(self.fc, 'instances')
self.m.StubOutWithMock(self.fc.instances, 'get')
self.fc.instances.get(12345).AndReturn(fake_dbinstance)
@ -312,8 +311,8 @@ class OSDBInstanceTest(HeatTestCase):
t = template_format.parse(db_template)
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
instance.resource_id = 12345
self.m.StubOutWithMock(instance, 'trove')
instance.trove().MultipleTimes().AndReturn(self.fc)
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
trove.TroveClientPlugin._create().AndReturn(self.fc)
self.m.StubOutWithMock(self.fc, 'instances')
self.m.StubOutWithMock(self.fc.instances, 'get')
self.fc.instances.get(12345).AndReturn(fake_dbinstance)
@ -419,8 +418,8 @@ class OSDBInstanceTest(HeatTestCase):
'datastore_type'] = 'mysql'
t['Resources']['MySqlCloudDB']['Properties'].pop('datastore_version')
instance = self._setup_test_clouddbinstance('dbinstance_test', t)
self.m.StubOutWithMock(instance, 'trove')
instance.trove().MultipleTimes().AndReturn(self.fc)
self.m.StubOutWithMock(trove.TroveClientPlugin, '_create')
trove.TroveClientPlugin._create().AndReturn(self.fc)
self.m.StubOutWithMock(self.fc, 'datastore_versions')
self.m.StubOutWithMock(self.fc.datastore_versions, 'list')
self.fc.datastore_versions.list(

View File

@ -28,7 +28,6 @@ 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.resources import nova_utils
from heat.engine.resources import server as servers
from heat.engine import scheduler
from heat.openstack.common.gettextutils import _
@ -175,12 +174,8 @@ class ServersTest(HeatTestCase):
glance.GlanceClientPlugin.get_image_id(image_id).AndRaise(exp)
def _mock_get_keypair_success(self, keypair_input, keypair):
n_cli_mock = self.m.CreateMockAnything()
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
nova.NovaClientPlugin._create().AndReturn(
n_cli_mock)
self.m.StubOutWithMock(nova_utils, 'get_keypair')
nova_utils.get_keypair(n_cli_mock, keypair_input).MultipleTimes().\
self.m.StubOutWithMock(nova.NovaClientPlugin, 'get_keypair')
nova.NovaClientPlugin.get_keypair(keypair_input).MultipleTimes().\
AndReturn(keypair)
def _server_validate_mock(self, server):
@ -763,8 +758,11 @@ class ServersTest(HeatTestCase):
web_server = tmpl.t['Resources']['WebServer']
del web_server['Properties']['image']
def create_server(device_name, mock_create=True):
self.m.UnsetStubs()
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
nova.NovaClientPlugin._create().AndReturn(self.fc)
self.m.ReplayAll()
def create_server(device_name):
web_server['Properties']['block_device_mapping'] = [{
"device_name": device_name,
"volume_id": "5d7e27da-6703-4f7e-9f94-1f67abef734c",
@ -773,17 +771,13 @@ class ServersTest(HeatTestCase):
resource_defns = tmpl.resource_definitions(stack)
server = servers.Server('server_with_bootable_volume',
resource_defns['WebServer'], stack)
if mock_create:
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
nova.NovaClientPlugin._create().AndReturn(self.fc)
self.m.ReplayAll()
return server
server = create_server(u'vda')
self.assertIsNone(server.validate())
server = create_server('vda', mock_create=False)
server = create_server('vda')
self.assertIsNone(server.validate())
server = create_server('vdb', mock_create=False)
server = create_server('vdb')
ex = self.assertRaises(exception.StackValidationFailed,
server.validate)
self.assertEqual('Neither image nor bootable volume is specified for '
@ -825,7 +819,9 @@ class ServersTest(HeatTestCase):
server = servers.Server('server_validate_test',
resource_defns['WebServer'], stack)
self.stub_ImageConstraint_validate()
self.m.StubOutWithMock(glance.ImageConstraint, "validate")
glance.ImageConstraint.validate(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(True)
self.m.ReplayAll()
self.assertIsNone(server.validate())
@ -1015,7 +1011,7 @@ class ServersTest(HeatTestCase):
new_meta = {'test': 123}
self.m.StubOutWithMock(self.fc.servers, 'set_meta')
self.fc.servers.set_meta(return_server,
nova_utils.meta_serialize(
server.client_plugin().meta_serialize(
new_meta)).AndReturn(None)
self.m.ReplayAll()
update_template = copy.deepcopy(server.t)
@ -1040,7 +1036,7 @@ class ServersTest(HeatTestCase):
# If we're going to call set_meta() directly we
# need to handle the serialization ourselves.
self.fc.servers.set_meta(return_server,
nova_utils.meta_serialize(
server.client_plugin().meta_serialize(
new_meta)).AndReturn(None)
self.m.ReplayAll()
update_template = copy.deepcopy(server.t)
@ -1229,7 +1225,9 @@ class ServersTest(HeatTestCase):
image_id = self.getUniqueString()
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
nova.NovaClientPlugin._create().AndReturn(self.fc)
self.stub_ImageConstraint_validate()
self.m.StubOutWithMock(glance.ImageConstraint, "validate")
glance.ImageConstraint.validate(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(True)
self.m.ReplayAll()
update_template = copy.deepcopy(server.t)
@ -1237,8 +1235,6 @@ class ServersTest(HeatTestCase):
updater = scheduler.TaskRunner(server.update, update_template)
self.assertRaises(resource.UpdateReplace, updater)
self.m.VerifyAll()
def _test_server_update_image_rebuild(self, status, policy='REBUILD'):
# Server.handle_update supports changing the image, and makes
# the change making a rebuild API call against Nova.
@ -1339,7 +1335,9 @@ class ServersTest(HeatTestCase):
server = self._create_test_server(return_server,
'update_prop')
self.stub_ImageConstraint_validate()
self.m.StubOutWithMock(glance.ImageConstraint, "validate")
glance.ImageConstraint.validate(
mox.IgnoreArg(), mox.IgnoreArg()).MultipleTimes().AndReturn(True)
self.m.ReplayAll()
update_template = copy.deepcopy(server.t)
@ -1347,8 +1345,6 @@ class ServersTest(HeatTestCase):
updater = scheduler.TaskRunner(server.update, update_template)
self.assertRaises(resource.UpdateReplace, updater)
self.m.VerifyAll()
def test_server_status_build(self):
return_server = self.fc.servers.list()[0]
server = self._setup_test_server(return_server,
@ -1857,8 +1853,6 @@ class ServersTest(HeatTestCase):
self.m.StubOutWithMock(self.fc.limits, 'get')
self.fc.limits.get().MultipleTimes().AndReturn(self.limits)
self.m.StubOutWithMock(server, 'nova')
server.nova().MultipleTimes().AndReturn(self.fc)
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
nova.NovaClientPlugin._create().AndReturn(self.fc)
self._mock_get_image_id_success('F17-x86_64-gold', 'image_id')
@ -1985,8 +1979,10 @@ class ServersTest(HeatTestCase):
"""The default value for instance_user in heat.conf is ec2-user."""
return_server = self.fc.servers.list()[1]
server = self._setup_test_server(return_server, 'default_user')
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(server,
metadata = server.metadata_get()
self.m.StubOutWithMock(nova.NovaClientPlugin, 'build_userdata')
nova.NovaClientPlugin.build_userdata(
metadata,
'wordpress',
instance_user='ec2-user',
user_data_format='HEAT_CFNTOOLS')
@ -2023,8 +2019,10 @@ class ServersTest(HeatTestCase):
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
nova.NovaClientPlugin._create().AndReturn(self.fc)
self._mock_get_image_id_success('F17-x86_64-gold', image_id)
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(server,
metadata = server.metadata_get()
self.m.StubOutWithMock(nova.NovaClientPlugin, 'build_userdata')
nova.NovaClientPlugin.build_userdata(
metadata,
'wordpress',
instance_user='custom_user',
user_data_format='HEAT_CFNTOOLS')
@ -2044,8 +2042,10 @@ class ServersTest(HeatTestCase):
server = self._setup_test_server(return_server, 'custom_user')
self.m.StubOutWithMock(servers.cfg.CONF, 'instance_user')
servers.cfg.CONF.instance_user = 'custom_user'
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(server,
metadata = server.metadata_get()
self.m.StubOutWithMock(nova.NovaClientPlugin, 'build_userdata')
nova.NovaClientPlugin.build_userdata(
metadata,
'wordpress',
instance_user='custom_user',
user_data_format='HEAT_CFNTOOLS')
@ -2067,8 +2067,10 @@ class ServersTest(HeatTestCase):
server = self._setup_test_server(return_server, 'custom_user')
self.m.StubOutWithMock(servers.cfg.CONF, 'instance_user')
servers.cfg.CONF.instance_user = ''
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(server,
metadata = server.metadata_get()
self.m.StubOutWithMock(nova.NovaClientPlugin, 'build_userdata')
nova.NovaClientPlugin.build_userdata(
metadata,
'wordpress',
instance_user=None,
user_data_format='HEAT_CFNTOOLS')
@ -2531,7 +2533,7 @@ class ServersTest(HeatTestCase):
# is NOT called during call to server.validate().
# This is the way to validate that no excessive calls to Nova
# are made during validation.
self.m.StubOutWithMock(nova_utils, 'absolute_limits')
self.m.StubOutWithMock(nova.NovaClientPlugin, 'absolute_limits')
self.m.StubOutWithMock(nova.NovaClientPlugin, '_create')
nova.NovaClientPlugin._create().AndReturn(self.fc)
self._mock_get_image_id_success('F17-x86_64-gold', 'image_id')

View File

@ -22,7 +22,6 @@ from heat.engine.clients.os import nova
from heat.engine import environment
from heat.engine import parser
from heat.engine.resources import instance as instances
from heat.engine.resources import nova_utils
from heat.engine import scheduler
from heat.tests.common import HeatTestCase
from heat.tests import utils
@ -152,13 +151,14 @@ class ServerTagsTest(HeatTestCase):
nova.NovaClientPlugin._create().AndReturn(self.fc)
self._mock_get_image_id_success('CentOS 5.2', 1)
# need to resolve the template functions
server_userdata = nova_utils.build_userdata(
instance,
metadata = instance.metadata_get()
server_userdata = instance.client_plugin().build_userdata(
metadata,
instance.t['Properties']['UserData'],
'ec2-user')
self.m.StubOutWithMock(nova_utils, 'build_userdata')
nova_utils.build_userdata(
instance,
self.m.StubOutWithMock(nova.NovaClientPlugin, 'build_userdata')
nova.NovaClientPlugin.build_userdata(
metadata,
instance.t['Properties']['UserData'],
'ec2-user').AndReturn(server_userdata)