Support stable/mitaka

Add support for stable/mitaka including new patches for ironic
and nova.

Change-Id: Ie566dd22aaddc30858438a98767ef962182e97d9
This commit is contained in:
Doug Szumski 2016-06-02 03:58:40 +01:00 committed by Bogun Dmitriy
parent 52d26cafdd
commit 65f5f65984
4 changed files with 249 additions and 195 deletions

View File

@ -31,6 +31,8 @@ from oslo_utils import fileutils
from oslo_log import log from oslo_log import log
from oslo_service import loopingcall from oslo_service import loopingcall
from ironic_lib import utils as ironic_utils
from ironic.common import boot_devices from ironic.common import boot_devices
from ironic.common import dhcp_factory from ironic.common import dhcp_factory
from ironic.common import exception from ironic.common import exception
@ -267,7 +269,7 @@ class BareonDeploy(base.DeployInterface):
pxe_info = self._get_tftp_image_info(task.node) pxe_info = self._get_tftp_image_info(task.node)
for label in pxe_info: for label in pxe_info:
path = pxe_info[label][1] path = pxe_info[label][1]
utils.unlink_without_raise(path) ironic_utils.unlink_without_raise(path)
AgentTFTPImageCache().clean_up() AgentTFTPImageCache().clean_up()
pxe_utils.clean_up_pxe_config(task) pxe_utils.clean_up_pxe_config(task)

View File

@ -23,6 +23,8 @@ from oslo_utils import fileutils
from oslo_log import log from oslo_log import log
from six.moves.urllib import parse from six.moves.urllib import parse
from ironic_lib import utils as ironic_utils
from ironic.common import exception from ironic.common import exception
from ironic.common import utils from ironic.common import utils
from ironic.common.i18n import _ from ironic.common.i18n import _
@ -135,7 +137,7 @@ def url_download_raw(context, node, url):
resource_path = url_download(context, node, url) resource_path = url_download(context, node, url)
with open(resource_path) as f: with open(resource_path) as f:
raw = f.read() raw = f.read()
utils.unlink_without_raise(resource_path) ironic_utils.unlink_without_raise(resource_path)
return raw return raw
@ -232,7 +234,7 @@ class Resource(bareon_utils.RawToPropertyMixin):
""" """
if not self.is_fetched(): if not self.is_fetched():
return return
utils.unlink_without_raise(self.local_path) ironic_utils.unlink_without_raise(self.local_path)
self._raw.pop('local_path', None) self._raw.pop('local_path', None)
@staticmethod @staticmethod
@ -497,7 +499,7 @@ class PullMountResource(Resource):
return return
bareon_utils.umount_without_raise(self.mount_point, '-fl') bareon_utils.umount_without_raise(self.mount_point, '-fl')
self._raw.pop('mount_point', None) self._raw.pop('mount_point', None)
utils.unlink_without_raise(self.local_path) ironic_utils.unlink_without_raise(self.local_path)
self._raw.pop('local_path', None) self._raw.pop('local_path', None)

View File

@ -1,8 +1,8 @@
diff --git a/ironic/api/controllers/v1/node.py b/ironic/api/controllers/v1/node.py diff --git a/ironic/api/controllers/v1/node.py b/ironic/api/controllers/v1/node.py
index d95298f..1b99c68 100644 index 30d52e8..0c66cb0 100644
--- a/ironic/api/controllers/v1/node.py --- a/ironic/api/controllers/v1/node.py
+++ b/ironic/api/controllers/v1/node.py +++ b/ironic/api/controllers/v1/node.py
@@ -452,8 +452,13 @@ class NodeStatesController(rest.RestController): @@ -472,8 +472,13 @@ class NodeStatesController(rest.RestController):
raise exception.NodeInMaintenance(op=_('provisioning'), raise exception.NodeInMaintenance(op=_('provisioning'),
node=rpc_node.uuid) node=rpc_node.uuid)
@ -16,7 +16,7 @@ index d95298f..1b99c68 100644
if not m.is_actionable_event(ir_states.VERBS.get(target, target)): if not m.is_actionable_event(ir_states.VERBS.get(target, target)):
# Normally, we let the task manager recognize and deal with # Normally, we let the task manager recognize and deal with
# NodeLocked exceptions. However, that isn't done until the RPC # NodeLocked exceptions. However, that isn't done until the RPC
@@ -470,6 +475,16 @@ class NodeStatesController(rest.RestController): @@ -490,6 +495,16 @@ class NodeStatesController(rest.RestController):
action=target, node=rpc_node.uuid, action=target, node=rpc_node.uuid,
state=rpc_node.provision_state) state=rpc_node.provision_state)
@ -34,7 +34,7 @@ index d95298f..1b99c68 100644
msg = (_('Adding a config drive is only supported when setting ' msg = (_('Adding a config drive is only supported when setting '
'provision state to %s') % ir_states.ACTIVE) 'provision state to %s') % ir_states.ACTIVE)
diff --git a/ironic/api/controllers/v1/utils.py b/ironic/api/controllers/v1/utils.py diff --git a/ironic/api/controllers/v1/utils.py b/ironic/api/controllers/v1/utils.py
index 538ca45..3715d41 100644 index 5a7f474..3f39f3f 100644
--- a/ironic/api/controllers/v1/utils.py --- a/ironic/api/controllers/v1/utils.py
+++ b/ironic/api/controllers/v1/utils.py +++ b/ironic/api/controllers/v1/utils.py
@@ -23,6 +23,7 @@ from webob.static import FileIter @@ -23,6 +23,7 @@ from webob.static import FileIter
@ -45,7 +45,7 @@ index 538ca45..3715d41 100644
from ironic.common import exception from ironic.common import exception
from ironic.common.i18n import _ from ironic.common.i18n import _
from ironic.common import states from ironic.common import states
@@ -109,7 +110,16 @@ def is_valid_node_name(name): @@ -122,7 +123,16 @@ def is_valid_node_name(name):
:param: name: the node name to check. :param: name: the node name to check.
:returns: True if the name is valid, False otherwise. :returns: True if the name is valid, False otherwise.
""" """
@ -74,11 +74,11 @@ index ccd2222..b8186c9 100644
- values.pop('tenant', None) - values.pop('tenant', None)
return cls(**values) return cls(**values)
diff --git a/ironic/common/images.py b/ironic/common/images.py diff --git a/ironic/common/images.py b/ironic/common/images.py
index 5b00e65..28e6bd7 100644 index 682a1a7..6685248 100644
--- a/ironic/common/images.py --- a/ironic/common/images.py
+++ b/ironic/common/images.py +++ b/ironic/common/images.py
@@ -328,16 +328,17 @@ def convert_image(source, dest, out_format, run_as_root=False): @@ -314,16 +314,17 @@ def create_isolinux_image_for_uefi(output_file, deploy_iso, kernel, ramdisk,
utils.execute(*cmd, run_as_root=run_as_root) raise exception.ImageCreationFailed(image_type='iso', error=e)
-def fetch(context, image_href, path, force_raw=False): -def fetch(context, image_href, path, force_raw=False):
@ -102,10 +102,10 @@ index 5b00e65..28e6bd7 100644
with fileutils.remove_path_on_error(path): with fileutils.remove_path_on_error(path):
with open(path, "wb") as image_file: with open(path, "wb") as image_file:
diff --git a/ironic/common/states.py b/ironic/common/states.py diff --git a/ironic/common/states.py b/ironic/common/states.py
index e61c807..2523a7f 100644 index 09c4aff..0e87cfd 100644
--- a/ironic/common/states.py --- a/ironic/common/states.py
+++ b/ironic/common/states.py +++ b/ironic/common/states.py
@@ -245,6 +245,9 @@ machine.add_state(INSPECTFAIL, target=MANAGEABLE, **watchers) @@ -246,6 +246,9 @@ machine.add_state(INSPECTFAIL, target=MANAGEABLE, **watchers)
# A deployment may fail # A deployment may fail
machine.add_transition(DEPLOYING, DEPLOYFAIL, 'fail') machine.add_transition(DEPLOYING, DEPLOYFAIL, 'fail')
@ -116,7 +116,7 @@ index e61c807..2523a7f 100644
# ironic/conductor/manager.py:do_node_deploy() # ironic/conductor/manager.py:do_node_deploy()
machine.add_transition(DEPLOYFAIL, DEPLOYING, 'rebuild') machine.add_transition(DEPLOYFAIL, DEPLOYING, 'rebuild')
diff --git a/ironic/common/swift.py b/ironic/common/swift.py diff --git a/ironic/common/swift.py b/ironic/common/swift.py
index 8fa2d65..14d6b55 100644 index b5c66c6..067d491 100644
--- a/ironic/common/swift.py --- a/ironic/common/swift.py
+++ b/ironic/common/swift.py +++ b/ironic/common/swift.py
@@ -24,6 +24,7 @@ from swiftclient import utils as swift_utils @@ -24,6 +24,7 @@ from swiftclient import utils as swift_utils
@ -141,41 +141,43 @@ index 8fa2d65..14d6b55 100644
CONF.import_opt('admin_user', 'keystonemiddleware.auth_token', CONF.import_opt('admin_user', 'keystonemiddleware.auth_token',
group='keystone_authtoken') group='keystone_authtoken')
CONF.import_opt('admin_tenant_name', 'keystonemiddleware.auth_token', CONF.import_opt('admin_tenant_name', 'keystonemiddleware.auth_token',
@@ -62,7 +70,9 @@ class SwiftAPI(object): @@ -63,7 +71,9 @@ class SwiftAPI(object):
tenant_name=CONF.keystone_authtoken.admin_tenant_name, key=None,
key=CONF.keystone_authtoken.admin_password, auth_url=None,
auth_url=CONF.keystone_authtoken.auth_uri, auth_version=None,
- auth_version=CONF.keystone_authtoken.auth_version): - region_name=None):
+ auth_version=CONF.keystone_authtoken.auth_version, + region_name=None,
+ preauthtoken=None, + preauthtoken=None,
+ preauthtenant=None): + preauthtenant=None):
"""Constructor for creating a SwiftAPI object. """Constructor for creating a SwiftAPI object.
:param user: the name of the user for Swift account :param user: the name of the user for Swift account
@@ -70,16 +80,41 @@ class SwiftAPI(object): @@ -72,6 +82,11 @@ class SwiftAPI(object):
:param key: the 'password' or key to authenticate with
:param auth_url: the url for authentication :param auth_url: the url for authentication
:param auth_version: the version of api to use for authentication :param auth_version: the version of api to use for authentication
:param region_name: the region used for getting endpoints of swift
+ :param preauthtoken: authentication token (if you have already + :param preauthtoken: authentication token (if you have already
+ authenticated) note authurl/user/key/tenant_name + authenticated) note authurl/user/key/tenant_name
+ are not required when specifying preauthtoken + are not required when specifying preauthtoken
+ :param preauthtenant a tenant that will be accessed using the + :param preauthtenant a tenant that will be accessed using the
+ preauthtoken + preauthtoken
""" """
- auth_url = keystone.get_keystone_url(auth_url, auth_version) user = user or CONF.keystone_authtoken.admin_user
- params = {'retries': CONF.swift.swift_max_retries, tenant_name = tenant_name or CONF.keystone_authtoken.admin_tenant_name
- 'insecure': CONF.keystone_authtoken.insecure, @@ -79,14 +94,34 @@ class SwiftAPI(object):
auth_url = auth_url or CONF.keystone_authtoken.auth_uri
auth_version = auth_version or CONF.keystone_authtoken.auth_version
auth_url = keystone.get_keystone_url(auth_url, auth_version)
+
params = {'retries': CONF.swift.swift_max_retries,
'insecure': CONF.keystone_authtoken.insecure,
- 'cacert': CONF.keystone_authtoken.cafile, - 'cacert': CONF.keystone_authtoken.cafile,
- 'user': user, - 'user': user,
- 'tenant_name': tenant_name, - 'tenant_name': tenant_name,
- 'key': key, - 'key': key,
- 'authurl': auth_url, - 'authurl': auth_url,
- 'auth_version': auth_version} - 'auth_version': auth_version}
+ params = { + 'cacert': CONF.keystone_authtoken.cafile}
+ 'retries': CONF.swift.swift_max_retries,
+ 'insecure': CONF.keystone_authtoken.insecure,
+ 'cacert': CONF.keystone_authtoken.cafile
+ }
+ +
+ if preauthtoken: + if preauthtoken:
+ # Determining swift url for the user's tenant account. + # Determining swift url for the user's tenant account.
@ -192,7 +194,6 @@ index 8fa2d65..14d6b55 100644
+ 'preauthurl': url + 'preauthurl': url
+ }) + })
+ else: + else:
+ auth_url = keystone.get_keystone_url(auth_url, auth_version)
+ params.update({ + params.update({
+ 'user': user, + 'user': user,
+ 'tenant_name': tenant_name, + 'tenant_name': tenant_name,
@ -200,10 +201,11 @@ index 8fa2d65..14d6b55 100644
+ 'authurl': auth_url, + 'authurl': auth_url,
+ 'auth_version': auth_version + 'auth_version': auth_version
+ }) + })
+
self.connection = swift_client.Connection(**params) region_name = region_name or CONF.keystone_authtoken.region_name
if region_name:
@@ -131,8 +166,8 @@ class SwiftAPI(object): params['os_options'] = {'region_name': region_name}
@@ -141,8 +176,8 @@ class SwiftAPI(object):
operation = _("head account") operation = _("head account")
raise exception.SwiftOperationError(operation=operation, raise exception.SwiftOperationError(operation=operation,
error=e) error=e)
@ -214,7 +216,7 @@ index 8fa2d65..14d6b55 100644
parse_result = parse.urlparse(storage_url) parse_result = parse.urlparse(storage_url)
swift_object_path = '/'.join((parse_result.path, container, object)) swift_object_path = '/'.join((parse_result.path, container, object))
temp_url_key = account_info['x-account-meta-temp-url-key'] temp_url_key = account_info['x-account-meta-temp-url-key']
@@ -189,3 +224,23 @@ class SwiftAPI(object): @@ -205,3 +240,23 @@ class SwiftAPI(object):
except swift_exceptions.ClientException as e: except swift_exceptions.ClientException as e:
operation = _("post object") operation = _("post object")
raise exception.SwiftOperationError(operation=operation, error=e) raise exception.SwiftOperationError(operation=operation, error=e)
@ -239,10 +241,10 @@ index 8fa2d65..14d6b55 100644
+ operation = _("get object") + operation = _("get object")
+ raise exception.SwiftOperationError(operation=operation, error=e) + raise exception.SwiftOperationError(operation=operation, error=e)
diff --git a/ironic/common/utils.py b/ironic/common/utils.py diff --git a/ironic/common/utils.py b/ironic/common/utils.py
index f863087..ed4398f 100644 index 9652a1b..7d17dcc 100644
--- a/ironic/common/utils.py --- a/ironic/common/utils.py
+++ b/ironic/common/utils.py +++ b/ironic/common/utils.py
@@ -42,6 +42,7 @@ from ironic.common import exception @@ -41,6 +41,7 @@ from ironic.common import exception
from ironic.common.i18n import _ from ironic.common.i18n import _
from ironic.common.i18n import _LE from ironic.common.i18n import _LE
from ironic.common.i18n import _LW from ironic.common.i18n import _LW
@ -250,8 +252,8 @@ index f863087..ed4398f 100644
utils_opts = [ utils_opts = [
cfg.StrOpt('rootwrap_config', cfg.StrOpt('rootwrap_config',
@@ -560,6 +561,11 @@ def is_http_url(url): @@ -560,6 +561,11 @@ def umount(loc, *args):
return url.startswith('http://') or url.startswith('https://') execute(*args, run_as_root=True, check_exit_code=[0])
+def get_tenant_id(tenant_name): +def get_tenant_id(tenant_name):
@ -263,10 +265,10 @@ index f863087..ed4398f 100644
"""Check a directory is usable. """Check a directory is usable.
diff --git a/ironic/conductor/manager.py b/ironic/conductor/manager.py diff --git a/ironic/conductor/manager.py b/ironic/conductor/manager.py
index b4bee31..e5bb190 100644 index 7e5679f..f419877 100644
--- a/ironic/conductor/manager.py --- a/ironic/conductor/manager.py
+++ b/ironic/conductor/manager.py +++ b/ironic/conductor/manager.py
@@ -768,6 +768,10 @@ class ConductorManager(periodic_task.PeriodicTasks): @@ -588,6 +588,11 @@ class ConductorManager(base_manager.BaseConductorManager):
""" """
LOG.debug("RPC do_node_tear_down called for node %s." % node_id) LOG.debug("RPC do_node_tear_down called for node %s." % node_id)
@ -274,14 +276,15 @@ index b4bee31..e5bb190 100644
+ if (task.node.provision_state == states.DEPLOYING and + if (task.node.provision_state == states.DEPLOYING and
+ task.driver.deploy.can_terminate_deployment): + task.driver.deploy.can_terminate_deployment):
+ task.driver.deploy.terminate_deployment(task) + task.driver.deploy.terminate_deployment(task)
+
with task_manager.acquire(context, node_id, shared=False, with task_manager.acquire(context, node_id, shared=False,
purpose='node tear down') as task: purpose='node tear down') as task:
try: try:
diff --git a/ironic/drivers/base.py b/ironic/drivers/base.py diff --git a/ironic/drivers/base.py b/ironic/drivers/base.py
index 098b7a0..6ebe05d 100644 index 02c3a75..15a2ce2 100644
--- a/ironic/drivers/base.py --- a/ironic/drivers/base.py
+++ b/ironic/drivers/base.py +++ b/ironic/drivers/base.py
@@ -345,6 +345,13 @@ class DeployInterface(BaseInterface): @@ -376,6 +376,13 @@ class DeployInterface(BaseInterface):
""" """
pass pass
@ -296,7 +299,7 @@ index 098b7a0..6ebe05d 100644
@six.add_metaclass(abc.ABCMeta) @six.add_metaclass(abc.ABCMeta)
class BootInterface(object): class BootInterface(object):
diff --git a/ironic/drivers/modules/image_cache.py b/ironic/drivers/modules/image_cache.py diff --git a/ironic/drivers/modules/image_cache.py b/ironic/drivers/modules/image_cache.py
index 8bb1e23..8e1a921 100644 index 1835425..7c45ef3 100644
--- a/ironic/drivers/modules/image_cache.py --- a/ironic/drivers/modules/image_cache.py
+++ b/ironic/drivers/modules/image_cache.py +++ b/ironic/drivers/modules/image_cache.py
@@ -27,6 +27,7 @@ from oslo_concurrency import lockutils @@ -27,6 +27,7 @@ from oslo_concurrency import lockutils
@ -317,7 +320,7 @@ index 8bb1e23..8e1a921 100644
"""Constructor. """Constructor.
:param master_dir: cache directory to work on :param master_dir: cache directory to work on
@@ -70,6 +72,7 @@ class ImageCache(object): @@ -71,6 +73,7 @@ class ImageCache(object):
self.master_dir = master_dir self.master_dir = master_dir
self._cache_size = cache_size self._cache_size = cache_size
self._cache_ttl = cache_ttl self._cache_ttl = cache_ttl
@ -325,7 +328,7 @@ index 8bb1e23..8e1a921 100644
if master_dir is not None: if master_dir is not None:
fileutils.ensure_tree(master_dir) fileutils.ensure_tree(master_dir)
@@ -94,23 +97,28 @@ class ImageCache(object): @@ -95,23 +98,28 @@ class ImageCache(object):
# NOTE(ghe): We don't share images between instances/hosts # NOTE(ghe): We don't share images between instances/hosts
if not CONF.parallel_image_downloads: if not CONF.parallel_image_downloads:
with lockutils.lock(img_download_lock_name, 'ironic-'): with lockutils.lock(img_download_lock_name, 'ironic-'):
@ -362,7 +365,7 @@ index 8bb1e23..8e1a921 100644
master_path = os.path.join(self.master_dir, master_file_name) master_path = os.path.join(self.master_dir, master_file_name)
if CONF.parallel_image_downloads: if CONF.parallel_image_downloads:
@@ -121,8 +129,8 @@ class ImageCache(object): @@ -122,8 +130,8 @@ class ImageCache(object):
# NOTE(vdrok): After rebuild requested image can change, so we # NOTE(vdrok): After rebuild requested image can change, so we
# should ensure that dest_path and master_path (if exists) are # should ensure that dest_path and master_path (if exists) are
# pointing to the same file and their content is up to date # pointing to the same file and their content is up to date
@ -373,7 +376,7 @@ index 8bb1e23..8e1a921 100644
dest_up_to_date = _delete_dest_path_if_stale(master_path, dest_up_to_date = _delete_dest_path_if_stale(master_path,
dest_path) dest_path)
@@ -168,7 +176,8 @@ class ImageCache(object): @@ -169,7 +177,8 @@ class ImageCache(object):
tmp_path = os.path.join(tmp_dir, href.split('/')[-1]) tmp_path = os.path.join(tmp_dir, href.split('/')[-1])
try: try:
@ -383,7 +386,7 @@ index 8bb1e23..8e1a921 100644
# NOTE(dtantsur): no need for global lock here - master_path # NOTE(dtantsur): no need for global lock here - master_path
# will have link count >1 at any moment, so won't be cleaned up # will have link count >1 at any moment, so won't be cleaned up
os.link(tmp_path, master_path) os.link(tmp_path, master_path)
@@ -308,10 +317,11 @@ def _free_disk_space_for(path): @@ -310,10 +319,11 @@ def _free_disk_space_for(path):
return stat.f_frsize * stat.f_bavail return stat.f_frsize * stat.f_bavail
@ -397,7 +400,7 @@ index 8bb1e23..8e1a921 100644
# Notes(yjiang5): If glance can provide the virtual size information, # Notes(yjiang5): If glance can provide the virtual size information,
# then we can firstly clean cache and then invoke images.fetch(). # then we can firstly clean cache and then invoke images.fetch().
if force_raw: if force_raw:
@@ -384,7 +394,7 @@ def cleanup(priority): @@ -386,7 +396,7 @@ def cleanup(priority):
return _add_property_to_class_func return _add_property_to_class_func
@ -406,7 +409,7 @@ index 8bb1e23..8e1a921 100644
"""Delete image from cache if it is not up to date with href contents. """Delete image from cache if it is not up to date with href contents.
:param master_path: path to an image in master cache :param master_path: path to an image in master cache
@@ -397,7 +407,8 @@ def _delete_master_path_if_stale(master_path, href, ctx): @@ -399,7 +409,8 @@ def _delete_master_path_if_stale(master_path, href, ctx):
# Glance image contents cannot be updated without changing image's UUID # Glance image contents cannot be updated without changing image's UUID
return os.path.exists(master_path) return os.path.exists(master_path)
if os.path.exists(master_path): if os.path.exists(master_path):
@ -416,11 +419,28 @@ index 8bb1e23..8e1a921 100644
img_mtime = img_service.show(href).get('updated_at') img_mtime = img_service.show(href).get('updated_at')
if not img_mtime: if not img_mtime:
# This means that href is not a glance image and doesn't have an # This means that href is not a glance image and doesn't have an
diff --git a/ironic/tests/common/test_swift.py b/ironic/tests/common/test_swift.py diff --git a/ironic/tests/unit/common/test_rpc.py b/ironic/tests/unit/common/test_rpc.py
index 43e3ef0..b2632c4 100644 index 6e10aac..5a8f316 100644
--- a/ironic/tests/common/test_swift.py --- a/ironic/tests/unit/common/test_rpc.py
+++ b/ironic/tests/common/test_swift.py +++ b/ironic/tests/unit/common/test_rpc.py
@@ -120,6 +120,7 @@ class SwiftTestCase(base.TestCase): @@ -65,10 +65,8 @@ class TestRequestContextSerializer(base.TestCase):
def test_deserialize_context(self):
self.context.user = 'fake-user'
- self.context.tenant = 'fake-tenant'
serialize_values = self.context.to_dict()
new_context = self.serializer.deserialize_context(serialize_values)
- # Ironic RequestContext from_dict will pop 'user' and 'tenant' and
- # initialize to None.
+ # Ironic RequestContext from_dict will pop 'user' and initialize
+ # to None.
self.assertIsNone(new_context.user)
- self.assertIsNone(new_context.tenant)
diff --git a/ironic/tests/unit/common/test_swift.py b/ironic/tests/unit/common/test_swift.py
index f696145..eb91895 100644
--- a/ironic/tests/unit/common/test_swift.py
+++ b/ironic/tests/unit/common/test_swift.py
@@ -127,6 +127,7 @@ class SwiftTestCase(base.TestCase):
connection_obj_mock.get_auth.return_value = auth connection_obj_mock.get_auth.return_value = auth
head_ret_val = {'x-account-meta-temp-url-key': 'secretkey'} head_ret_val = {'x-account-meta-temp-url-key': 'secretkey'}
connection_obj_mock.head_account.return_value = head_ret_val connection_obj_mock.head_account.return_value = head_ret_val
@ -428,10 +448,10 @@ index 43e3ef0..b2632c4 100644
gen_temp_url_mock.return_value = 'temp-url-path' gen_temp_url_mock.return_value = 'temp-url-path'
temp_url_returned = swiftapi.get_temp_url('container', 'object', 10) temp_url_returned = swiftapi.get_temp_url('container', 'object', 10)
connection_obj_mock.get_auth.assert_called_once_with() connection_obj_mock.get_auth.assert_called_once_with()
diff --git a/ironic/tests/drivers/test_image_cache.py b/ironic/tests/drivers/test_image_cache.py diff --git a/ironic/tests/unit/drivers/modules/test_image_cache.py b/ironic/tests/unit/drivers/modules/test_image_cache.py
index 3d666cd..436aa49 100644 index 1224f52..ed9d83c 100644
--- a/ironic/tests/drivers/test_image_cache.py --- a/ironic/tests/unit/drivers/modules/test_image_cache.py
+++ b/ironic/tests/drivers/test_image_cache.py +++ b/ironic/tests/unit/drivers/modules/test_image_cache.py
@@ -59,7 +59,7 @@ class TestImageCacheFetch(base.TestCase): @@ -59,7 +59,7 @@ class TestImageCacheFetch(base.TestCase):
self.cache.fetch_image(self.uuid, self.dest_path) self.cache.fetch_image(self.uuid, self.dest_path)
self.assertFalse(mock_download.called) self.assertFalse(mock_download.called)
@ -486,7 +506,7 @@ index 3d666cd..436aa49 100644
self.assertEqual(self.uuid, uuid) self.assertEqual(self.uuid, uuid)
self.assertNotEqual(self.dest_path, tmp_path) self.assertNotEqual(self.dest_path, tmp_path)
self.assertNotEqual(os.path.dirname(tmp_path), self.master_dir) self.assertNotEqual(os.path.dirname(tmp_path), self.master_dir)
@@ -430,7 +430,7 @@ class TestImageCacheCleanUp(base.TestCase): @@ -446,7 +446,7 @@ class TestImageCacheCleanUp(base.TestCase):
@mock.patch.object(utils, 'rmtree_without_raise', autospec=True) @mock.patch.object(utils, 'rmtree_without_raise', autospec=True)
@mock.patch.object(image_cache, '_fetch', autospec=True) @mock.patch.object(image_cache, '_fetch', autospec=True)
def test_temp_images_not_cleaned(self, mock_fetch, mock_rmtree): def test_temp_images_not_cleaned(self, mock_fetch, mock_rmtree):
@ -495,7 +515,7 @@ index 3d666cd..436aa49 100644
with open(tmp_path, 'w') as fp: with open(tmp_path, 'w') as fp:
fp.write("TEST" * 10) fp.write("TEST" * 10)
@@ -675,7 +675,8 @@ class TestFetchCleanup(base.TestCase): @@ -691,7 +691,8 @@ class TestFetchCleanup(base.TestCase):
mock_size.return_value = 100 mock_size.return_value = 100
image_cache._fetch('fake', 'fake-uuid', '/foo/bar', force_raw=True) image_cache._fetch('fake', 'fake-uuid', '/foo/bar', force_raw=True)
mock_fetch.assert_called_once_with('fake', 'fake-uuid', mock_fetch.assert_called_once_with('fake', 'fake-uuid',
@ -505,4 +525,3 @@ index 3d666cd..436aa49 100644
mock_clean.assert_called_once_with('/foo', 100) mock_clean.assert_called_once_with('/foo', 100)
mock_raw.assert_called_once_with('fake-uuid', '/foo/bar', mock_raw.assert_called_once_with('fake-uuid', '/foo/bar',
'/foo/bar.part') '/foo/bar.part')

View File

@ -1,8 +1,8 @@
diff --git a/nova/objects/image_meta.py b/nova/objects/image_meta.py diff --git a/nova/objects/image_meta.py b/nova/objects/image_meta.py
index 15be3f1..83fc2fb 100644 index 72a16dc..1003c14 100644
--- a/nova/objects/image_meta.py --- a/nova/objects/image_meta.py
+++ b/nova/objects/image_meta.py +++ b/nova/objects/image_meta.py
@@ -346,6 +346,7 @@ class ImageMetaProps(base.NovaObject): @@ -414,6 +414,7 @@ class ImageMetaProps(base.NovaObject):
# is a fairly generic type. For a detailed type consider os_distro # is a fairly generic type. For a detailed type consider os_distro
# instead # instead
'os_type': fields.OSTypeField(), 'os_type': fields.OSTypeField(),
@ -11,23 +11,23 @@ index 15be3f1..83fc2fb 100644
# The keys are the legacy property names and # The keys are the legacy property names and
diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py
index 031555f..e0368b8 100644 index 199e351..456c64e 100644
--- a/nova/tests/unit/objects/test_objects.py --- a/nova/tests/unit/objects/test_objects.py
+++ b/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py
@@ -1180,7 +1180,7 @@ object_data = { @@ -1126,7 +1126,7 @@ object_data = {
'HostMapping': '1.0-1a3390a696792a552ab7bd31a77ba9ac', 'HyperVLiveMigrateData': '1.0-0b868dd6228a09c3f3e47016dddf6a1c',
'HVSpec': '1.1-6b4f7c0f688cbd03e24142a44eb9010d', 'HVSpec': '1.2-db672e73304da86139086d003f3977e7',
'ImageMeta': '1.7-642d1b2eb3e880a367f37d72dd76162d', 'ImageMeta': '1.8-642d1b2eb3e880a367f37d72dd76162d',
- 'ImageMetaProps': '1.7-f12fc4cf3e25d616f69a66fb9d2a7aa6', - 'ImageMetaProps': '1.12-6a132dee47931447bf86c03c7006d96c',
+ 'ImageMetaProps': '1.7-716042e9e80ea16890f475200940d6f9', + 'ImageMetaProps': '1.12-605af4f965158bd805edb60d77678e9f',
'Instance': '2.0-ff56804dce87d81d9a04834d4bd1e3d2', 'Instance': '2.1-416fdd0dfc33dfa12ff2cfdd8cc32e17',
# NOTE(danms): Reviewers: do not approve changes to the Instance1 'InstanceAction': '1.1-f9f293e526b66fca0d05c3b3a2d13914',
# object schema. It is frozen for Liberty and will be removed in 'InstanceActionEvent': '1.1-e56a64fa4710e43ef7af2ad9d6028b33',
diff --git a/nova/tests/unit/virt/ironic/test_driver.py b/nova/tests/unit/virt/ironic/test_driver.py diff --git a/nova/tests/unit/virt/ironic/test_driver.py b/nova/tests/unit/virt/ironic/test_driver.py
index a8c653a..940497c 100644 index e2a6902..8d8e47f 100644
--- a/nova/tests/unit/virt/ironic/test_driver.py --- a/nova/tests/unit/virt/ironic/test_driver.py
+++ b/nova/tests/unit/virt/ironic/test_driver.py +++ b/nova/tests/unit/virt/ironic/test_driver.py
@@ -799,6 +799,7 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -800,6 +800,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
result = self.driver.macs_for_instance(instance) result = self.driver.macs_for_instance(instance)
self.assertIsNone(result) self.assertIsNone(result)
@ -35,7 +35,7 @@ index a8c653a..940497c 100644
@mock.patch.object(objects.Instance, 'save') @mock.patch.object(objects.Instance, 'save')
@mock.patch.object(loopingcall, 'FixedIntervalLoopingCall') @mock.patch.object(loopingcall, 'FixedIntervalLoopingCall')
@mock.patch.object(FAKE_CLIENT, 'node') @mock.patch.object(FAKE_CLIENT, 'node')
@@ -807,7 +808,7 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -808,7 +809,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
@mock.patch.object(ironic_driver.IronicDriver, '_plug_vifs') @mock.patch.object(ironic_driver.IronicDriver, '_plug_vifs')
@mock.patch.object(ironic_driver.IronicDriver, '_start_firewall') @mock.patch.object(ironic_driver.IronicDriver, '_start_firewall')
def _test_spawn(self, mock_sf, mock_pvifs, mock_adf, mock_wait_active, def _test_spawn(self, mock_sf, mock_pvifs, mock_adf, mock_wait_active,
@ -44,7 +44,7 @@ index a8c653a..940497c 100644
node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid) node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid)
instance = fake_instance.fake_instance_obj(self.ctx, node=node_uuid) instance = fake_instance.fake_instance_obj(self.ctx, node=node_uuid)
@@ -845,6 +846,7 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -846,6 +847,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
fake_looping_call.start.assert_called_once_with( fake_looping_call.start.assert_called_once_with(
interval=CONF.ironic.api_retry_interval) interval=CONF.ironic.api_retry_interval)
fake_looping_call.wait.assert_called_once_with() fake_looping_call.wait.assert_called_once_with()
@ -52,10 +52,11 @@ index a8c653a..940497c 100644
@mock.patch.object(ironic_driver.IronicDriver, '_generate_configdrive') @mock.patch.object(ironic_driver.IronicDriver, '_generate_configdrive')
@mock.patch.object(configdrive, 'required_by') @mock.patch.object(configdrive, 'required_by')
@@ -897,14 +899,62 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -898,13 +900,61 @@ class IronicDriverTestCase(test.NoDBTestCase):
self.driver.spawn, self.ctx, instance, None, [], None) self.driver.spawn, self.ctx, instance, None, [], None)
mock_destroy.assert_called_once_with(self.ctx, instance, None) self.assertEqual(0, mock_destroy.call_count)
- def _test_add_driver_fields(self, mock_update=None, mock_call=None):
+ @mock.patch.object(FAKE_CLIENT, 'node') + @mock.patch.object(FAKE_CLIENT, 'node')
+ def test__get_switch_boot_options(self, mock_node): + def test__get_switch_boot_options(self, mock_node):
+ node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' + node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
@ -96,21 +97,18 @@ index a8c653a..940497c 100644
+ +
+ @mock.patch.object(ironic_driver.IronicDriver, + @mock.patch.object(ironic_driver.IronicDriver,
+ "_get_deploy_config_options") + "_get_deploy_config_options")
@mock.patch.object(FAKE_CLIENT.node, 'update') + def _test_add_driver_fields(self, mock_get_depl_conf_opts,
- def test__add_driver_fields_good(self, mock_update): + mock_update=None, mock_call=None):
+ def test__add_driver_fields_good(self, mock_update,
+ mock_get_depl_conf_opts):
node = ironic_utils.get_test_node(driver='fake') node = ironic_utils.get_test_node(driver='fake')
- instance = fake_instance.fake_instance_obj(self.ctx, - instance = fake_instance.fake_instance_obj(self.ctx,
- node=node.uuid) - node=node.uuid)
- image_meta = ironic_utils.get_test_image_meta_object()
+ instance = fake_instance.fake_instance_obj( + instance = fake_instance.fake_instance_obj(
+ self.ctx, + self.ctx,
+ node=node.uuid, + node=node.uuid,
+ expected_attrs=('metadata',)) + expected_attrs=('metadata',))
image_meta = ironic_utils.get_test_image_meta()
flavor = ironic_utils.get_test_flavor() flavor = ironic_utils.get_test_flavor()
+ +
+ image_meta = ironic_utils.get_test_image_meta_object()
+ mock_get_depl_conf_opts.return_value = {'foo': 'bar123'} + mock_get_depl_conf_opts.return_value = {'foo': 'bar123'}
+ instance['metadata']['driver_actions'] = 'test_driver_actions' + instance['metadata']['driver_actions'] = 'test_driver_actions'
+ +
@ -119,7 +117,7 @@ index a8c653a..940497c 100644
expected_patch = [{'path': '/instance_info/image_source', 'op': 'add', expected_patch = [{'path': '/instance_info/image_source', 'op': 'add',
'value': image_meta.id}, 'value': image_meta.id},
{'path': '/instance_info/root_gb', 'op': 'add', {'path': '/instance_info/root_gb', 'op': 'add',
@@ -920,21 +970,96 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -920,7 +970,14 @@ class IronicDriverTestCase(test.NoDBTestCase):
{'path': '/instance_info/local_gb', 'op': 'add', {'path': '/instance_info/local_gb', 'op': 'add',
'value': str(node.properties.get('local_gb', 0))}, 'value': str(node.properties.get('local_gb', 0))},
{'path': '/instance_uuid', 'op': 'add', {'path': '/instance_uuid', 'op': 'add',
@ -132,9 +130,10 @@ index a8c653a..940497c 100644
+ 'op': 'add', + 'op': 'add',
+ 'value': 'test_driver_actions'}, + 'value': 'test_driver_actions'},
+ ] + ]
mock_update.assert_called_once_with(node.uuid, expected_patch)
@mock.patch.object(FAKE_CLIENT.node, 'update') if mock_call is not None:
# assert call() is invoked with retry_on_conflict False to
@@ -943,14 +1000,82 @@ class IronicDriverTestCase(test.NoDBTestCase):
def test__add_driver_fields_fail(self, mock_update): def test__add_driver_fields_fail(self, mock_update):
mock_update.side_effect = ironic_exception.BadRequest() mock_update.side_effect = ironic_exception.BadRequest()
node = ironic_utils.get_test_node(driver='fake') node = ironic_utils.get_test_node(driver='fake')
@ -144,7 +143,7 @@ index a8c653a..940497c 100644
+ self.ctx, + self.ctx,
+ node=node.uuid, + node=node.uuid,
+ expected_attrs=('metadata',)) + expected_attrs=('metadata',))
image_meta = ironic_utils.get_test_image_meta_object() image_meta = ironic_utils.get_test_image_meta()
flavor = ironic_utils.get_test_flavor() flavor = ironic_utils.get_test_flavor()
self.assertRaises(exception.InstanceDeployFailure, self.assertRaises(exception.InstanceDeployFailure,
self.driver._add_driver_fields, self.driver._add_driver_fields,
@ -153,7 +152,7 @@ index a8c653a..940497c 100644
+ def test__get_deploy_config_options_all_present(self): + def test__get_deploy_config_options_all_present(self):
+ node = ironic_utils.get_test_node( + node = ironic_utils.get_test_node(
+ driver='fake', driver_info={'deploy_config': "node-conf"}) + driver='fake', driver_info={'deploy_config': "node-conf"})
+ image_meta = ironic_utils.get_test_image_meta_object( + image_meta = ironic_utils.get_test_image_meta(
+ deploy_config="image-conf") + deploy_config="image-conf")
+ instance = fake_instance.fake_instance_obj( + instance = fake_instance.fake_instance_obj(
+ self.ctx, node=node.uuid, expected_attrs=('metadata',), + self.ctx, node=node.uuid, expected_attrs=('metadata',),
@ -178,7 +177,7 @@ index a8c653a..940497c 100644
+ "image": "previous_image_conf", + "image": "previous_image_conf",
+ }} + }}
+ ) + )
+ image_meta = ironic_utils.get_test_image_meta_object( + image_meta = ironic_utils.get_test_image_meta(
+ deploy_config="image-conf") + deploy_config="image-conf")
+ instance = fake_instance.fake_instance_obj( + instance = fake_instance.fake_instance_obj(
+ self.ctx, node=node.uuid, expected_attrs=('metadata',)) + self.ctx, node=node.uuid, expected_attrs=('metadata',))
@ -193,7 +192,7 @@ index a8c653a..940497c 100644
+ +
+ def test__get_deploy_config_options_some_present(self): + def test__get_deploy_config_options_some_present(self):
+ node = ironic_utils.get_test_node(driver='fake') + node = ironic_utils.get_test_node(driver='fake')
+ image_meta = ironic_utils.get_test_image_meta_object() + image_meta = ironic_utils.get_test_image_meta()
+ instance = fake_instance.fake_instance_obj( + instance = fake_instance.fake_instance_obj(
+ self.ctx, node=node.uuid, expected_attrs=('metadata',), + self.ctx, node=node.uuid, expected_attrs=('metadata',),
+ metadata={'deploy_config': "instance-conf"}) + metadata={'deploy_config': "instance-conf"})
@ -206,7 +205,7 @@ index a8c653a..940497c 100644
+ +
+ def test__get_deploy_config_options_none_present(self): + def test__get_deploy_config_options_none_present(self):
+ node = ironic_utils.get_test_node(driver='fake') + node = ironic_utils.get_test_node(driver='fake')
+ image_meta = ironic_utils.get_test_image_meta_object() + image_meta = ironic_utils.get_test_image_meta()
+ instance = fake_instance.fake_instance_obj( + instance = fake_instance.fake_instance_obj(
+ self.ctx, node=node.uuid, expected_attrs=('metadata',)) + self.ctx, node=node.uuid, expected_attrs=('metadata',))
+ +
@ -216,23 +215,10 @@ index a8c653a..940497c 100644
+ expected = {} + expected = {}
+ self.assertEqual(expected, opts) + self.assertEqual(expected, opts)
+ +
@mock.patch.object(FAKE_CLIENT.node, 'update') @mock.patch.object(configdrive, 'required_by')
def test__cleanup_deploy_good_with_flavor(self, mock_update): @mock.patch.object(FAKE_CLIENT, 'node')
node = ironic_utils.get_test_node(driver='fake', def test_spawn_node_driver_validation_fail(self, mock_node,
@@ -983,8 +1108,10 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -959,7 +1084,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
node = ironic_utils.get_test_node(driver='fake',
instance_uuid=self.instance_uuid)
flavor = ironic_utils.get_test_flavor(extra_specs={})
- instance = fake_instance.fake_instance_obj(self.ctx,
- node=node.uuid)
+ instance = fake_instance.fake_instance_obj(
+ self.ctx,
+ node=node.uuid,
+ expected_attrs=('metadata',))
instance.flavor = flavor
self.assertRaises(exception.InstanceTerminationFailure,
self.driver._cleanup_deploy,
@@ -998,7 +1125,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid) node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid)
flavor = ironic_utils.get_test_flavor() flavor = ironic_utils.get_test_flavor()
@ -242,7 +228,7 @@ index a8c653a..940497c 100644
instance.flavor = flavor instance.flavor = flavor
mock_node.validate.return_value = ironic_utils.get_test_validation( mock_node.validate.return_value = ironic_utils.get_test_validation(
@@ -1023,7 +1151,8 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -985,7 +1111,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid) node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid)
flavor = ironic_utils.get_test_flavor() flavor = ironic_utils.get_test_flavor()
@ -252,7 +238,27 @@ index a8c653a..940497c 100644
instance.flavor = flavor instance.flavor = flavor
mock_node.get.return_value = node mock_node.get.return_value = node
mock_node.validate.return_value = ironic_utils.get_test_validation() mock_node.validate.return_value = ironic_utils.get_test_validation()
@@ -1053,7 +1182,8 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -1018,7 +1145,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid)
flavor = ironic_utils.get_test_flavor()
- instance = fake_instance.fake_instance_obj(self.ctx, node=node_uuid)
+ instance = fake_instance.fake_instance_obj(
+ self.ctx, node=node_uuid, expected_attrs=('metadata',))
instance.flavor = flavor
mock_node.get.return_value = node
mock_node.validate.return_value = ironic_utils.get_test_validation()
@@ -1034,8 +1162,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
mock_node.get.assert_called_once_with(
node_uuid, fields=ironic_driver._NODE_FIELDS)
mock_node.validate.assert_called_once_with(node_uuid)
- mock_cleanup_deploy.assert_called_with(self.ctx, node, instance, None,
- flavor=flavor)
+ mock_cleanup_deploy.assert_called_with(node, instance, None)
@mock.patch.object(configdrive, 'required_by')
@mock.patch.object(FAKE_CLIENT, 'node')
@@ -1049,7 +1176,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid) node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid)
flavor = ironic_utils.get_test_flavor() flavor = ironic_utils.get_test_flavor()
@ -262,7 +268,7 @@ index a8c653a..940497c 100644
instance.flavor = flavor instance.flavor = flavor
image_meta = ironic_utils.get_test_image_meta() image_meta = ironic_utils.get_test_image_meta()
@@ -1082,7 +1212,8 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -1077,7 +1205,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid) node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid)
flavor = ironic_utils.get_test_flavor() flavor = ironic_utils.get_test_flavor()
@ -272,7 +278,7 @@ index a8c653a..940497c 100644
instance.flavor = flavor instance.flavor = flavor
image_meta = ironic_utils.get_test_image_meta() image_meta = ironic_utils.get_test_image_meta()
@@ -1113,7 +1244,8 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -1107,7 +1236,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid) node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid)
flavor = ironic_utils.get_test_flavor() flavor = ironic_utils.get_test_flavor()
@ -282,7 +288,7 @@ index a8c653a..940497c 100644
instance.flavor = flavor instance.flavor = flavor
image_meta = ironic_utils.get_test_image_meta() image_meta = ironic_utils.get_test_image_meta()
@@ -1146,7 +1278,8 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -1139,7 +1269,8 @@ class IronicDriverTestCase(test.NoDBTestCase):
node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' node_uuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'
node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid) node = ironic_utils.get_test_node(driver='fake', uuid=node_uuid)
flavor = ironic_utils.get_test_flavor(ephemeral_gb=1) flavor = ironic_utils.get_test_flavor(ephemeral_gb=1)
@ -292,7 +298,7 @@ index a8c653a..940497c 100644
instance.flavor = flavor instance.flavor = flavor
mock_node.get_by_instance_uuid.return_value = node mock_node.get_by_instance_uuid.return_value = node
mock_node.set_provision_state.return_value = mock.MagicMock() mock_node.set_provision_state.return_value = mock.MagicMock()
@@ -1541,15 +1674,16 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -1529,15 +1660,16 @@ class IronicDriverTestCase(test.NoDBTestCase):
self.driver.refresh_instance_security_rules(fake_group) self.driver.refresh_instance_security_rules(fake_group)
mock_risr.assert_called_once_with(fake_group) mock_risr.assert_called_once_with(fake_group)
@ -311,7 +317,7 @@ index a8c653a..940497c 100644
node_uuid = uuidutils.generate_uuid() node_uuid = uuidutils.generate_uuid()
node = ironic_utils.get_test_node(uuid=node_uuid, node = ironic_utils.get_test_node(uuid=node_uuid,
instance_uuid=self.instance_uuid, instance_uuid=self.instance_uuid,
@@ -1560,10 +1694,12 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -1548,10 +1680,12 @@ class IronicDriverTestCase(test.NoDBTestCase):
flavor_id = 5 flavor_id = 5
flavor = objects.Flavor(flavor_id=flavor_id, name='baremetal') flavor = objects.Flavor(flavor_id=flavor_id, name='baremetal')
@ -328,7 +334,7 @@ index a8c653a..940497c 100644
instance.flavor = flavor instance.flavor = flavor
fake_looping_call = FakeLoopingCall() fake_looping_call = FakeLoopingCall()
@@ -1589,6 +1725,7 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -1575,6 +1709,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
fake_looping_call.start.assert_called_once_with( fake_looping_call.start.assert_called_once_with(
interval=CONF.ironic.api_retry_interval) interval=CONF.ironic.api_retry_interval)
fake_looping_call.wait.assert_called_once_with() fake_looping_call.wait.assert_called_once_with()
@ -336,7 +342,7 @@ index a8c653a..940497c 100644
def test_rebuild_preserve_ephemeral(self): def test_rebuild_preserve_ephemeral(self):
self._test_rebuild(preserve=True) self._test_rebuild(preserve=True)
@@ -1598,7 +1735,7 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -1584,7 +1719,7 @@ class IronicDriverTestCase(test.NoDBTestCase):
@mock.patch.object(FAKE_CLIENT.node, 'set_provision_state') @mock.patch.object(FAKE_CLIENT.node, 'set_provision_state')
@mock.patch.object(ironic_driver.IronicDriver, '_add_driver_fields') @mock.patch.object(ironic_driver.IronicDriver, '_add_driver_fields')
@ -345,7 +351,7 @@ index a8c653a..940497c 100644
@mock.patch.object(objects.Instance, 'save') @mock.patch.object(objects.Instance, 'save')
def test_rebuild_failures(self, mock_save, mock_get, mock_driver_fields, def test_rebuild_failures(self, mock_save, mock_get, mock_driver_fields,
mock_set_pstate): mock_set_pstate):
@@ -1612,10 +1749,12 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -1598,10 +1733,12 @@ class IronicDriverTestCase(test.NoDBTestCase):
flavor_id = 5 flavor_id = 5
flavor = objects.Flavor(flavor_id=flavor_id, name='baremetal') flavor = objects.Flavor(flavor_id=flavor_id, name='baremetal')
@ -362,7 +368,7 @@ index a8c653a..940497c 100644
instance.flavor = flavor instance.flavor = flavor
exceptions = [ exceptions = [
@@ -1631,6 +1770,316 @@ class IronicDriverTestCase(test.NoDBTestCase): @@ -1617,6 +1754,316 @@ class IronicDriverTestCase(test.NoDBTestCase):
injected_files=None, admin_password=None, bdms=None, injected_files=None, admin_password=None, bdms=None,
detach_block_devices=None, attach_block_devices=None) detach_block_devices=None, attach_block_devices=None)
@ -386,7 +392,7 @@ index a8c653a..940497c 100644
+ instance_info={'multiboot': True}) + instance_info={'multiboot': True})
+ mock_get.return_value = node + mock_get.return_value = node
+ +
+ image_meta = ironic_utils.get_test_image_meta_object() + image_meta = ironic_utils.get_test_image_meta()
+ +
+ flavor_id = 5 + flavor_id = 5
+ flavor = objects.Flavor(flavor_id=flavor_id, name='baremetal') + flavor = objects.Flavor(flavor_id=flavor_id, name='baremetal')
@ -437,7 +443,7 @@ index a8c653a..940497c 100644
+ instance_info={'multiboot': True}) + instance_info={'multiboot': True})
+ mock_get.return_value = mock_get_by_instance.return_value = node + mock_get.return_value = mock_get_by_instance.return_value = node
+ +
+ image_meta = ironic_utils.get_test_image_meta_object() + image_meta = ironic_utils.get_test_image_meta()
+ mock_image_meta_from_dict.return_value = image_meta + mock_image_meta_from_dict.return_value = image_meta
+ +
+ flavor_id = 5 + flavor_id = 5
@ -469,7 +475,7 @@ index a8c653a..940497c 100644
+ instance_type_id=5, + instance_type_id=5,
+ instance_info={'multiboot': True}) + instance_info={'multiboot': True})
+ +
+ image_meta = ironic_utils.get_test_image_meta_object() + image_meta = ironic_utils.get_test_image_meta()
+ flavor_id = 5 + flavor_id = 5
+ instance = fake_instance.fake_instance_obj( + instance = fake_instance.fake_instance_obj(
+ self.ctx, + self.ctx,
@ -544,7 +550,7 @@ index a8c653a..940497c 100644
+ 'image_source': 'original_image', + 'image_source': 'original_image',
+ }) + })
+ +
+ image_meta = ironic_utils.get_test_image_meta_object() + image_meta = ironic_utils.get_test_image_meta()
+ flavor_id = 5 + flavor_id = 5
+ instance = fake_instance.fake_instance_obj( + instance = fake_instance.fake_instance_obj(
+ self.ctx, + self.ctx,
@ -582,7 +588,7 @@ index a8c653a..940497c 100644
+ 'image_source': 'original_image', + 'image_source': 'original_image',
+ }) + })
+ +
+ image_meta = ironic_utils.get_test_image_meta_object() + image_meta = ironic_utils.get_test_image_meta()
+ flavor_id = 5 + flavor_id = 5
+ instance = fake_instance.fake_instance_obj( + instance = fake_instance.fake_instance_obj(
+ self.ctx, + self.ctx,
@ -618,7 +624,7 @@ index a8c653a..940497c 100644
+ 'image_source': 'original_image', + 'image_source': 'original_image',
+ }) + })
+ +
+ image_meta = ironic_utils.get_test_image_meta_object() + image_meta = ironic_utils.get_test_image_meta()
+ flavor_id = 5 + flavor_id = 5
+ instance = fake_instance.fake_instance_obj( + instance = fake_instance.fake_instance_obj(
+ self.ctx, + self.ctx,
@ -655,7 +661,7 @@ index a8c653a..940497c 100644
+ 'image_source': 'original_image', + 'image_source': 'original_image',
+ }) + })
+ +
+ image_meta = ironic_utils.get_test_image_meta_object() + image_meta = ironic_utils.get_test_image_meta()
+ flavor_id = 5 + flavor_id = 5
+ instance = fake_instance.fake_instance_obj( + instance = fake_instance.fake_instance_obj(
+ self.ctx, + self.ctx,
@ -676,11 +682,11 @@ index a8c653a..940497c 100644
+ instance.metadata['switch_boot_error']) + instance.metadata['switch_boot_error'])
+ mock_save.assert_called_once_with() + mock_save.assert_called_once_with()
+ +
@mock.patch.object(FAKE_CLIENT.node, 'get')
@mock.patch.object(instance_metadata, 'InstanceMetadata') def _test_network_binding_host_id(self, is_neutron, mock_get):
@mock.patch.object(configdrive, 'ConfigDriveBuilder') node_uuid = uuidutils.generate_uuid()
diff --git a/nova/tests/unit/virt/ironic/utils.py b/nova/tests/unit/virt/ironic/utils.py diff --git a/nova/tests/unit/virt/ironic/utils.py b/nova/tests/unit/virt/ironic/utils.py
index 0e67919..66eede3 100644 index d25930e..3a37e2e 100644
--- a/nova/tests/unit/virt/ironic/utils.py --- a/nova/tests/unit/virt/ironic/utils.py
+++ b/nova/tests/unit/virt/ironic/utils.py +++ b/nova/tests/unit/virt/ironic/utils.py
@@ -39,7 +39,7 @@ def get_test_node(**kw): @@ -39,7 +39,7 @@ def get_test_node(**kw):
@ -692,20 +698,23 @@ index 0e67919..66eede3 100644
'driver': kw.get('driver', 'fake'), 'driver': kw.get('driver', 'fake'),
'driver_info': kw.get('driver_info', {}), 'driver_info': kw.get('driver_info', {}),
'properties': kw.get('properties', {}), 'properties': kw.get('properties', {}),
@@ -91,7 +91,11 @@ def get_test_flavor(**kw): @@ -92,8 +92,13 @@ def get_test_flavor(**kw):
def get_test_image_meta(**kw): def get_test_image_meta(**kw):
- return {'id': kw.get('id', 'cccccccc-cccc-cccc-cccc-cccccccccccc')} - return objects.ImageMeta.from_dict(
+ return {'id': kw.get('id', 'cccccccc-cccc-cccc-cccc-cccccccccccc'), - {'id': kw.get('id', 'cccccccc-cccc-cccc-cccc-cccccccccccc')})
+ return objects.ImageMeta.from_dict({
+ 'id': kw.get('id', 'cccccccc-cccc-cccc-cccc-cccccccccccc'),
+ 'properties': { + 'properties': {
+ 'deploy_config': kw.get('deploy_config', ''), + 'deploy_config': kw.get('deploy_config', ''),
+ 'driver_actions': kw.get('driver_actions', ''), + 'driver_actions': kw.get('driver_actions', '')
+ }} + }
+ })
def get_test_image_meta_object(**kw): class FakePortClient(object):
@@ -134,6 +138,9 @@ class FakeNodeClient(object): @@ -131,6 +136,9 @@ class FakeNodeClient(object):
def validate(self, node_uuid): def validate(self, node_uuid):
pass pass
@ -716,10 +725,19 @@ index 0e67919..66eede3 100644
class FakeClient(object): class FakeClient(object):
diff --git a/nova/virt/ironic/driver.py b/nova/virt/ironic/driver.py diff --git a/nova/virt/ironic/driver.py b/nova/virt/ironic/driver.py
index 194221e..062f3d7 100644 index 9cbf4d7..6b5eed9 100644
--- a/nova/virt/ironic/driver.py --- a/nova/virt/ironic/driver.py
+++ b/nova/virt/ironic/driver.py +++ b/nova/virt/ironic/driver.py
@@ -391,6 +391,17 @@ class IronicDriver(virt_driver.ComputeDriver): @@ -76,7 +76,7 @@ _UNPROVISION_STATES = (ironic_states.ACTIVE, ironic_states.DEPLOYFAIL,
_NODE_FIELDS = ('uuid', 'power_state', 'target_power_state', 'provision_state',
'target_provision_state', 'last_error', 'maintenance',
- 'properties', 'instance_uuid')
+ 'properties', 'instance_uuid', 'instance_info', 'driver_info')
def map_power_state(state):
@@ -357,6 +357,17 @@ class IronicDriver(virt_driver.ComputeDriver):
# Associate the node with an instance # Associate the node with an instance
patch.append({'path': '/instance_uuid', 'op': 'add', patch.append({'path': '/instance_uuid', 'op': 'add',
'value': instance.uuid}) 'value': instance.uuid})
@ -735,9 +753,9 @@ index 194221e..062f3d7 100644
+ 'value': instance.metadata.get('driver_actions', '')}) + 'value': instance.metadata.get('driver_actions', '')})
+ +
try: try:
self.ironicclient.call('node.update', node.uuid, patch) # FIXME(lucasagomes): The "retry_on_conflict" parameter was added
except ironic.exc.BadRequest: # to basically causes the deployment to fail faster in case the
@@ -400,6 +411,12 @@ class IronicDriver(virt_driver.ComputeDriver): @@ -371,6 +382,12 @@ class IronicDriver(virt_driver.ComputeDriver):
LOG.error(msg) LOG.error(msg)
raise exception.InstanceDeployFailure(msg) raise exception.InstanceDeployFailure(msg)
@ -747,10 +765,20 @@ index 194221e..062f3d7 100644
+ 'value': image_meta.id}] + 'value': image_meta.id}]
+ self.ironicclient.call('node.update', node.uuid, patch) + self.ironicclient.call('node.update', node.uuid, patch)
+ +
def _cleanup_deploy(self, context, node, instance, network_info, def _cleanup_deploy(self, node, instance, network_info):
flavor=None): self._unplug_vifs(node, instance, network_info)
if flavor is None: self._stop_firewall(instance, network_info)
@@ -807,9 +824,19 @@ class IronicDriver(virt_driver.ComputeDriver): @@ -744,8 +761,7 @@ class IronicDriver(virt_driver.ComputeDriver):
msg = (_LE("Failed to build configdrive: %s") %
six.text_type(e))
LOG.error(msg, instance=instance)
- self._cleanup_deploy(context, node, instance, network_info,
- flavor=flavor)
+ self._cleanup_deploy(node, instance, network_info)
LOG.info(_LI("Config drive for instance %(instance)s on "
"baremetal node %(node)s created."),
@@ -753,9 +769,19 @@ class IronicDriver(virt_driver.ComputeDriver):
# trigger the node deploy # trigger the node deploy
try: try:
@ -773,10 +801,10 @@ index 194221e..062f3d7 100644
except Exception as e: except Exception as e:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
msg = (_LE("Failed to request Ironic to provision instance " msg = (_LE("Failed to request Ironic to provision instance "
@@ -834,6 +861,17 @@ class IronicDriver(virt_driver.ComputeDriver): @@ -777,6 +803,17 @@ class IronicDriver(virt_driver.ComputeDriver):
"baremetal node %(node)s."),
{'instance': instance.uuid, {'instance': instance.uuid,
'node': node_uuid}) 'node': node_uuid})
self.destroy(context, instance, network_info)
+ else: + else:
+ self._get_switch_boot_options(context, instance, node_uuid) + self._get_switch_boot_options(context, instance, node_uuid)
+ +
@ -789,14 +817,14 @@ index 194221e..062f3d7 100644
+ multiboot_meta.get('elements', [])] + multiboot_meta.get('elements', [])]
+ instance.metadata['available_images'] = str(available_images) + instance.metadata['available_images'] = str(available_images)
def _unprovision(self, ironicclient, instance, node): def _unprovision(self, instance, node):
"""This method is called from destroy() to unprovision """This method is called from destroy() to unprovision
@@ -1188,16 +1226,102 @@ class IronicDriver(virt_driver.ComputeDriver): @@ -1113,16 +1150,102 @@ class IronicDriver(virt_driver.ComputeDriver):
instance.task_state = task_states.REBUILD_SPAWNING instance.task_state = task_states.REBUILD_SPAWNING
instance.save(expected_task_state=[task_states.REBUILDING]) instance.save(expected_task_state=[task_states.REBUILDING])
- node_uuid = instance.node - node_uuid = instance.node
- node = self.ironicclient.call("node.get", node_uuid) - node = self._get_node(node_uuid)
+ # NOTE(oberezovskyi): Required to get real node uuid assigned to nova + # NOTE(oberezovskyi): Required to get real node uuid assigned to nova
+ # instance. Workaround after + # instance. Workaround after
+ # Change-Id: I0233f964d8f294f0ffd9edcb16b1aaf93486177f + # Change-Id: I0233f964d8f294f0ffd9edcb16b1aaf93486177f
@ -881,7 +909,7 @@ index 194221e..062f3d7 100644
+ else: + else:
+ raise exception.InvalidMetadata( + raise exception.InvalidMetadata(
+ reason="To trigger switch boot device flow, both 'sb_user' " + reason="To trigger switch boot device flow, both 'sb_user' "
+ "and 'sb_key' metadata params are required. To "s + "and 'sb_key' metadata params are required. To "
+ "trigger a standard rebuild flow, use " + "trigger a standard rebuild flow, use "
+ "force_rebuild=True metadata flag.") + "force_rebuild=True metadata flag.")
+ +
@ -901,11 +929,10 @@ index 194221e..062f3d7 100644
except (exception.NovaException, # Retry failed except (exception.NovaException, # Retry failed
ironic.exc.InternalServerError, # Validations ironic.exc.InternalServerError, # Validations
ironic.exc.BadRequest) as e: # Maintenance ironic.exc.BadRequest) as e: # Maintenance
@@ -1213,3 +1337,22 @@ class IronicDriver(virt_driver.ComputeDriver): @@ -1138,6 +1261,25 @@ class IronicDriver(virt_driver.ComputeDriver):
instance)
timer.start(interval=CONF.ironic.api_retry_interval).wait() timer.start(interval=CONF.ironic.api_retry_interval).wait()
LOG.info(_LI('Instance was successfully rebuilt'), instance=instance) LOG.info(_LI('Instance was successfully rebuilt'), instance=instance)
+
+ def _get_deploy_config_options(self, node, instance, image_meta): + def _get_deploy_config_options(self, node, instance, image_meta):
+ # Taking into account previous options, if any. This is to support + # Taking into account previous options, if any. This is to support
+ # rebuild flow where the user might or might not pass deploy_config + # rebuild flow where the user might or might not pass deploy_config
@ -924,3 +951,7 @@ index 194221e..062f3d7 100644
+ # Override previous by current. + # Override previous by current.
+ res.update(curr_options) + res.update(curr_options)
+ return res + return res
+
def network_binding_host_id(self, context, instance):
"""Get host ID to associate with network ports.