Remove deprecated nova_utils
Remove unused nova_utils and test_nova_utils according to https://etherpad.openstack.org/p/YVR-heat-liberty-deprecation Change-Id: Id9cc30fddb604a1c1d63017cd2085c1f6a4ab9a3
This commit is contained in:
@@ -1,396 +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.
|
||||
"""Utilities for Resources that use the OpenStack Nova API."""
|
||||
|
||||
import email
|
||||
from email.mime import multipart
|
||||
from email.mime import text
|
||||
import os
|
||||
import pkgutil
|
||||
import string
|
||||
import warnings
|
||||
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_serialization import jsonutils
|
||||
import six
|
||||
from six.moves.urllib import parse as urlparse
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common.i18n import _
|
||||
from heat.common.i18n import _LW
|
||||
from heat.engine import scheduler
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
deferred_server_statuses = ['BUILD',
|
||||
'HARD_REBOOT',
|
||||
'PASSWORD',
|
||||
'REBOOT',
|
||||
'RESCUE',
|
||||
'RESIZE',
|
||||
'REVERT_RESIZE',
|
||||
'SHUTOFF',
|
||||
'SUSPENDED',
|
||||
'VERIFY_RESIZE']
|
||||
|
||||
|
||||
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:
|
||||
LOG.warn(_LW("Server %(name)s (%(id)s) received an OverLimit "
|
||||
"response during server.get(): %(exception)s"),
|
||||
{'name': server.name,
|
||||
'id': server.id,
|
||||
'exception': exc})
|
||||
except nova_exceptions.ClientException as exc:
|
||||
http_status = (getattr(exc, 'http_status', None) or
|
||||
getattr(exc, 'code', None))
|
||||
if http_status in (500, 503):
|
||||
LOG.warn(_LW('Server "%(name)s" (%(id)s) received the following '
|
||||
'exception during server.get(): %(exception)s'),
|
||||
{'name': server.name,
|
||||
'id': server.id,
|
||||
'exception': exc})
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
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:
|
||||
return ip['addr']
|
||||
|
||||
|
||||
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.
|
||||
|
||||
:param nova_client: the nova client to use
|
||||
:param flavor: the name of the flavor to find
|
||||
:returns: the id of :flavor:
|
||||
:raises: exception.FlavorMissing
|
||||
'''
|
||||
flavor_id = None
|
||||
flavor_list = nova_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(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:
|
||||
|
||||
:param nova_client: the nova client to use
|
||||
: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 nova_client.keypairs.get(key_name)
|
||||
except nova_exceptions.NotFound:
|
||||
raise exception.UserKeyPairMissing(key_name=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.
|
||||
|
||||
: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 = text.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'))
|
||||
|
||||
metadata = resource.metadata_get()
|
||||
if metadata:
|
||||
attachments.append((jsonutils.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 = multipart.MIMEMultipart(_subparts=subparts)
|
||||
|
||||
return mime_blob.as_string()
|
||||
|
||||
|
||||
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:
|
||||
server.delete()
|
||||
except nova_exceptions.NotFound:
|
||||
return
|
||||
|
||||
while True:
|
||||
yield
|
||||
|
||||
try:
|
||||
refresh_server(server)
|
||||
except nova_exceptions.NotFound:
|
||||
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(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)
|
||||
|
||||
|
||||
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
|
||||
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(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)
|
||||
|
||||
|
||||
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
|
||||
refresh_server(server)
|
||||
if server.status == 'ERROR':
|
||||
raise exception.Error(
|
||||
_("Rebuilding server failed, status '%s'") % server.status)
|
||||
|
||||
|
||||
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 jsonutils.dumps(value))
|
||||
) for (key, value) in metadata.items())
|
||||
|
||||
|
||||
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 six.iterkeys(current_md) if key not in metadata]
|
||||
if len(to_del) > 0:
|
||||
client.servers.delete_meta(server, to_del)
|
||||
|
||||
client.servers.set_meta(server, metadata)
|
||||
|
||||
|
||||
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:
|
||||
LOG.warn(_LW('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(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)])
|
||||
@@ -10,7 +10,7 @@
|
||||
# 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'."""
|
||||
"""Tests for :module:'heat.engine.clients.os.nova'."""
|
||||
|
||||
import collections
|
||||
import uuid
|
||||
@@ -40,7 +40,7 @@ class NovaClientPluginTestCase(common.HeatTestCase):
|
||||
class NovaClientPluginTests(NovaClientPluginTestCase):
|
||||
"""
|
||||
Basic tests for the helper methods in
|
||||
:module:'heat.engine.nova_utils'.
|
||||
:module:'heat.engine.clients.os.nova'.
|
||||
"""
|
||||
|
||||
def test_get_ip(self):
|
||||
|
||||
@@ -1,269 +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.
|
||||
|
||||
"""Tests for :module:'heat.engine.resources.nova_utls'."""
|
||||
|
||||
import uuid
|
||||
|
||||
import mock
|
||||
from novaclient import exceptions as nova_exceptions
|
||||
import six
|
||||
|
||||
from heat.common import exception
|
||||
from heat.engine import nova_utils
|
||||
from heat.engine import scheduler
|
||||
from heat.tests import common
|
||||
from heat.tests.nova import fakes as fakes_nova
|
||||
|
||||
|
||||
class NovaUtilsTests(common.HeatTestCase):
|
||||
"""
|
||||
Basic tests for the helper methods in
|
||||
:module:'heat.engine.nova_utils'.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(NovaUtilsTests, self).setUp()
|
||||
self.nova_client = self.m.CreateMockAnything()
|
||||
|
||||
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 = nova_utils.get_ip(my_image, 'public', 4)
|
||||
self.assertEqual(expected, observed)
|
||||
|
||||
expected = '10.13.12.13'
|
||||
observed = nova_utils.get_ip(my_image, 'private', 4)
|
||||
self.assertEqual(expected, observed)
|
||||
|
||||
expected = '2401:1801:7800:0101:c058:dd33:ff18:04e6'
|
||||
observed = nova_utils.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, nova_utils.get_flavor_id(self.nova_client,
|
||||
flav_name))
|
||||
self.assertEqual(flav_id, nova_utils.get_flavor_id(self.nova_client,
|
||||
flav_id))
|
||||
self.assertRaises(exception.FlavorMissing, nova_utils.get_flavor_id,
|
||||
self.nova_client, '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(fakes_nova.fake_exception())
|
||||
self.m.ReplayAll()
|
||||
self.assertEqual(my_key, nova_utils.get_keypair(self.nova_client,
|
||||
my_key_name))
|
||||
self.assertRaises(exception.UserKeyPairMissing, nova_utils.get_keypair,
|
||||
self.nova_client, 'notakey')
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_delete_server(self):
|
||||
server = mock.Mock()
|
||||
server.status = "DELETED"
|
||||
task = scheduler.TaskRunner(nova_utils.delete_server, server)
|
||||
self.assertIsNone(task())
|
||||
|
||||
def test_delete_server_notfound(self):
|
||||
server = mock.Mock()
|
||||
server.delete.side_effect = nova_exceptions.NotFound(404)
|
||||
task = scheduler.TaskRunner(nova_utils.delete_server, server)
|
||||
self.assertIsNone(task())
|
||||
|
||||
def test_delete_noserver(self):
|
||||
task = scheduler.TaskRunner(nova_utils.delete_server, None)
|
||||
self.assertIsNone(task())
|
||||
|
||||
def test_delete_servererror(self):
|
||||
server = mock.Mock()
|
||||
server.name = "myserver"
|
||||
server.status = "ERROR"
|
||||
server.fault = {
|
||||
"message": "test error",
|
||||
}
|
||||
task = scheduler.TaskRunner(nova_utils.delete_server, server)
|
||||
err = self.assertRaises(exception.Error, task)
|
||||
self.assertIn("myserver delete failed: (None) test error",
|
||||
six.text_type(err))
|
||||
|
||||
|
||||
class NovaUtilsRefreshServerTests(common.HeatTestCase):
|
||||
|
||||
def test_successful_refresh(self):
|
||||
server = self.m.CreateMockAnything()
|
||||
server.get().AndReturn(None)
|
||||
self.m.ReplayAll()
|
||||
|
||||
self.assertIsNone(nova_utils.refresh_server(server))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_overlimit_error(self):
|
||||
server = mock.Mock()
|
||||
server.get.side_effect = fakes_nova.fake_exception(413)
|
||||
self.assertIsNone(nova_utils.refresh_server(server))
|
||||
|
||||
def test_500_error(self):
|
||||
server = self.m.CreateMockAnything()
|
||||
server.get().AndRaise(fakes_nova.fake_exception(500))
|
||||
self.m.ReplayAll()
|
||||
|
||||
self.assertIsNone(nova_utils.refresh_server(server))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_503_error(self):
|
||||
server = self.m.CreateMockAnything()
|
||||
server.get().AndRaise(fakes_nova.fake_exception(503))
|
||||
self.m.ReplayAll()
|
||||
|
||||
self.assertIsNone(nova_utils.refresh_server(server))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_unhandled_exception(self):
|
||||
server = self.m.CreateMockAnything()
|
||||
server.get().AndRaise(fakes_nova.fake_exception(501))
|
||||
self.m.ReplayAll()
|
||||
|
||||
self.assertRaises(nova_exceptions.ClientException,
|
||||
nova_utils.refresh_server, server)
|
||||
self.m.VerifyAll()
|
||||
|
||||
|
||||
class NovaUtilsUserdataTests(common.HeatTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(NovaUtilsUserdataTests, self).setUp()
|
||||
self.nova_client = self.m.CreateMockAnything()
|
||||
|
||||
def test_build_userdata(self):
|
||||
"""Tests the build_userdata function."""
|
||||
resource = self.m.CreateMockAnything()
|
||||
resource.metadata_get().AndReturn({})
|
||||
self.m.StubOutWithMock(nova_utils.cfg, 'CONF')
|
||||
cnf = nova_utils.cfg.CONF
|
||||
cnf.heat_metadata_server_url = 'http://server.test:123'
|
||||
cnf.heat_watch_server_url = 'http://server.test:345'
|
||||
cnf.instance_connection_is_secure = False
|
||||
cnf.instance_connection_https_validate_certificates = False
|
||||
self.m.ReplayAll()
|
||||
data = nova_utils.build_userdata(resource)
|
||||
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)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_build_userdata_without_instance_user(self):
|
||||
"""Don't add a custom instance user when not requested."""
|
||||
resource = self.m.CreateMockAnything()
|
||||
resource.metadata_get().AndReturn({})
|
||||
self.m.StubOutWithMock(nova_utils.cfg, 'CONF')
|
||||
cnf = nova_utils.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'
|
||||
self.m.ReplayAll()
|
||||
data = nova_utils.build_userdata(resource, instance_user=None)
|
||||
self.assertNotIn('user: ', data)
|
||||
self.assertNotIn('useradd', data)
|
||||
self.assertNotIn('config_instance_user', data)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_build_userdata_with_instance_user(self):
|
||||
"""Add the custom instance user when requested."""
|
||||
resource = self.m.CreateMockAnything()
|
||||
resource.metadata_get().AndReturn(None)
|
||||
self.m.StubOutWithMock(nova_utils.cfg, 'CONF')
|
||||
cnf = nova_utils.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'
|
||||
self.m.ReplayAll()
|
||||
data = nova_utils.build_userdata(resource,
|
||||
instance_user="custominstanceuser")
|
||||
self.assertNotIn('config_instance_user', data)
|
||||
self.assertIn("custominstanceuser", data)
|
||||
self.m.VerifyAll()
|
||||
|
||||
|
||||
class NovaUtilsMetadataTests(common.HeatTestCase):
|
||||
|
||||
def test_serialize_string(self):
|
||||
original = {'test_key': 'simple string value'}
|
||||
self.assertEqual(original, nova_utils.meta_serialize(original))
|
||||
|
||||
def test_serialize_int(self):
|
||||
original = {'test_key': 123}
|
||||
expected = {'test_key': '123'}
|
||||
self.assertEqual(expected, nova_utils.meta_serialize(original))
|
||||
|
||||
def test_serialize_list(self):
|
||||
original = {'test_key': [1, 2, 3]}
|
||||
expected = {'test_key': '[1, 2, 3]'}
|
||||
self.assertEqual(expected, nova_utils.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, nova_utils.meta_serialize(original))
|
||||
|
||||
def test_serialize_none(self):
|
||||
original = {'test_key': None}
|
||||
expected = {'test_key': 'null'}
|
||||
self.assertEqual(expected, nova_utils.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, nova_utils.meta_serialize(original))
|
||||
@@ -3227,9 +3227,9 @@ class ServersTest(common.HeatTestCase):
|
||||
server = servers.Server('server_create_image_err',
|
||||
resource_defns['WebServer'], stack)
|
||||
|
||||
# We mock out nova_utils.absolute_limits but we don't specify
|
||||
# how this mock should behave, so mox will verify that this mock
|
||||
# is NOT called during call to server.validate().
|
||||
# We mock out nova.NovaClientPlugin.absolute_limits but we don't
|
||||
# specify how this mock should behave, so mox will verify that this
|
||||
# mock 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.NovaClientPlugin, 'absolute_limits')
|
||||
|
||||
Reference in New Issue
Block a user