summaryrefslogtreecommitdiff
path: root/heat
diff options
context:
space:
mode:
Diffstat (limited to 'heat')
-rw-r--r--heat/common/context.py16
-rw-r--r--heat/db/sqlalchemy/api.py24
-rw-r--r--heat/engine/check_resource.py4
-rw-r--r--heat/engine/clients/client_plugin.py2
-rw-r--r--heat/engine/clients/os/blazar.py15
-rw-r--r--heat/engine/clients/os/designate.py2
-rw-r--r--heat/engine/clients/os/nova.py36
-rw-r--r--heat/engine/resource.py17
-rw-r--r--heat/engine/resources/openstack/neutron/firewall.py4
-rw-r--r--heat/engine/resources/openstack/nova/flavor.py2
-rw-r--r--heat/engine/resources/openstack/nova/floatingip.py2
-rw-r--r--heat/engine/resources/openstack/nova/host_aggregate.py2
-rw-r--r--heat/engine/resources/openstack/nova/keypair.py2
-rw-r--r--heat/engine/resources/openstack/nova/quota.py26
-rw-r--r--heat/engine/resources/openstack/nova/server_group.py2
-rw-r--r--heat/engine/resources/openstack/nova/server_network_mixin.py6
-rw-r--r--heat/engine/resources/stack_resource.py9
-rw-r--r--heat/engine/service.py10
-rw-r--r--heat/engine/worker.py3
-rw-r--r--heat/tests/clients/test_nova_client.py37
-rw-r--r--heat/tests/engine/test_engine_worker.py23
-rw-r--r--heat/tests/openstack/nova/fakes.py13
-rw-r--r--heat/tests/openstack/nova/test_flavor.py3
-rw-r--r--heat/tests/openstack/nova/test_host_aggregate.py3
-rw-r--r--heat/tests/openstack/nova/test_keypair.py2
-rw-r--r--heat/tests/openstack/nova/test_quota.py3
-rw-r--r--heat/tests/openstack/nova/test_server.py28
-rw-r--r--heat/tests/openstack/nova/test_server_group.py3
28 files changed, 147 insertions, 152 deletions
diff --git a/heat/common/context.py b/heat/common/context.py
index c2b48611a..0911982c3 100644
--- a/heat/common/context.py
+++ b/heat/common/context.py
@@ -12,7 +12,6 @@
# under the License.
from keystoneauth1 import access
-from keystoneauth1 import exceptions as ksa_exceptions
from keystoneauth1.identity import access as access_plugin
from keystoneauth1.identity import generic
from keystoneauth1 import loading as ks_loading
@@ -25,7 +24,6 @@ import oslo_messaging
from oslo_middleware import request_id as oslo_request_id
from oslo_utils import importutils
import six
-import tenacity
from heat.common import config
from heat.common import endpoint_utils
@@ -38,6 +36,8 @@ from heat.engine import clients
LOG = logging.getLogger(__name__)
+cfg.CONF.import_opt('client_retry_limit', 'heat.common.config')
+
# Note, we yield the options via list_opts to enable generation of the
# sample heat.conf, but we don't register these options directly via
# cfg.CONF.register*, it's done via ks_loading.register_auth_conf_options
@@ -53,15 +53,6 @@ TRUSTEE_CONF_GROUP = 'trustee'
ks_loading.register_auth_conf_options(cfg.CONF, TRUSTEE_CONF_GROUP)
-retry_on_connection_timeout = tenacity.retry(
- stop=tenacity.stop_after_attempt(cfg.CONF.client_retry_limit+1),
- wait=tenacity.wait_random(max=2),
- retry=tenacity.retry_if_exception_type(
- (ksa_exceptions.ConnectFailure,
- ksa_exceptions.DiscoveryFailure)),
- reraise=True)
-
-
def list_opts():
trustee_opts = ks_loading.get_auth_common_conf_options()
trustee_opts.extend(ks_loading.get_auth_plugin_conf_options(
@@ -122,6 +113,7 @@ class RequestContext(context.RequestContext):
self._session = None
self._clients = None
self._keystone_session = session.Session(
+ connect_retries=cfg.CONF.client_retry_limit,
**config.get_ssl_options('keystone'))
self.trust_id = trust_id
self.trustor_user_id = trustor_user_id
@@ -299,8 +291,6 @@ class RequestContext(context.RequestContext):
class StoredContext(RequestContext):
-
- @retry_on_connection_timeout
def _load_keystone_data(self):
self._keystone_loaded = True
auth_ref = self.auth_plugin.get_access(self.keystone_session)
diff --git a/heat/db/sqlalchemy/api.py b/heat/db/sqlalchemy/api.py
index c3f31ddfc..a33b39d12 100644
--- a/heat/db/sqlalchemy/api.py
+++ b/heat/db/sqlalchemy/api.py
@@ -164,7 +164,11 @@ def raw_template_update(context, template_id, values):
def raw_template_delete(context, template_id):
- raw_template = raw_template_get(context, template_id)
+ try:
+ raw_template = raw_template_get(context, template_id)
+ except exception.NotFound:
+ # Ignore not found
+ return
raw_tmpl_files_id = raw_template.files_id
session = context.session
with session.begin(subtransactions=True):
@@ -267,7 +271,7 @@ def resource_purge_deleted(context, stack_id):
query = context.session.query(models.Resource)
result = query.filter_by(**filters)
attr_ids = [r.attr_data_id for r in result if r.attr_data_id is not None]
- with context.session.begin(subtransactions=True):
+ with context.session.begin():
result.delete()
if attr_ids:
context.session.query(models.ResourcePropertiesData).filter(
@@ -320,7 +324,7 @@ def resource_delete(context, resource_id):
def resource_attr_id_set(context, resource_id, atomic_key, attr_id):
session = context.session
- with session.begin(subtransactions=True):
+ with session.begin():
values = {'attr_data_id': attr_id}
_add_atomic_key_to_values(values, atomic_key)
rows_updated = session.query(models.Resource).filter(and_(
@@ -346,7 +350,7 @@ def resource_attr_id_set(context, resource_id, atomic_key, attr_id):
def resource_attr_data_delete(context, resource_id, attr_id):
session = context.session
- with session.begin(subtransactions=True):
+ with session.begin():
resource = session.query(models.Resource).get(resource_id)
attr_prop_data = session.query(
models.ResourcePropertiesData).get(attr_id)
@@ -468,7 +472,7 @@ def resource_exchange_stacks(context, resource_id1, resource_id2):
def resource_data_delete(context, resource_id, key):
result = resource_data_get_by_key(context, resource_id, key)
session = context.session
- with session.begin(subtransactions=True):
+ with session.begin():
session.delete(result)
@@ -486,7 +490,7 @@ def resource_create_replacement(context,
atomic_key, expected_engine_id=None):
session = context.session
try:
- with session.begin(subtransactions=True):
+ with session.begin():
new_res = resource_create(context, new_res_values)
update_data = {'replaced_by': new_res.id}
update_data.update(existing_res_values)
@@ -1267,7 +1271,7 @@ def software_deployment_update(context, deployment_id, values):
def software_deployment_delete(context, deployment_id):
deployment = software_deployment_get(context, deployment_id)
session = context.session
- with session.begin(subtransactions=True):
+ with session.begin():
session.delete(deployment)
@@ -1631,7 +1635,7 @@ def _db_encrypt_or_decrypt_template_params(
batch_size=batch_size)
next_batch = list(itertools.islice(template_batches, batch_size))
while next_batch:
- with session.begin(subtransactions=True):
+ with session.begin():
for raw_template in next_batch:
try:
if verbose:
@@ -1714,7 +1718,7 @@ def _db_encrypt_or_decrypt_resource_prop_data_legacy(
batch_size=batch_size)
next_batch = list(itertools.islice(resource_batches, batch_size))
while next_batch:
- with session.begin(subtransactions=True):
+ with session.begin():
for resource in next_batch:
if not resource.properties_data:
continue
@@ -1761,7 +1765,7 @@ def _db_encrypt_or_decrypt_resource_prop_data(
model=models.ResourcePropertiesData, batch_size=batch_size)
next_batch = list(itertools.islice(rpd_batches, batch_size))
while next_batch:
- with session.begin(subtransactions=True):
+ with session.begin():
for rpd in next_batch:
if not rpd.data:
continue
diff --git a/heat/engine/check_resource.py b/heat/engine/check_resource.py
index a2f6d842c..7d12ceeec 100644
--- a/heat/engine/check_resource.py
+++ b/heat/engine/check_resource.py
@@ -163,6 +163,8 @@ class CheckResource(object):
return True
except exception.UpdateInProgress:
+ LOG.debug('Waiting for existing update to unlock resource %s',
+ rsrc.id)
if self._stale_resource_needs_retry(cnxt, rsrc, prev_template_id):
rpc_data = sync_point.serialize_input_data(self.input_data)
self._rpc_client.check_resource(cnxt,
@@ -170,6 +172,8 @@ class CheckResource(object):
current_traversal,
rpc_data, is_update,
adopt_stack_data)
+ else:
+ rsrc.handle_preempt()
except exception.ResourceFailure as ex:
action = ex.action or rsrc.action
reason = 'Resource %s failed: %s' % (action,
diff --git a/heat/engine/clients/client_plugin.py b/heat/engine/clients/client_plugin.py
index 6f485d56a..ab737da6f 100644
--- a/heat/engine/clients/client_plugin.py
+++ b/heat/engine/clients/client_plugin.py
@@ -24,7 +24,6 @@ import requests
import six
from heat.common import config
-from heat.common import context
from heat.common import exception as heat_exception
cfg.CONF.import_opt('client_retry_limit', 'heat.common.config')
@@ -94,7 +93,6 @@ class ClientPlugin(object):
def url_for(self, **kwargs):
keystone_session = self.context.keystone_session
- @context.retry_on_connection_timeout
def get_endpoint():
return keystone_session.get_endpoint(**kwargs)
diff --git a/heat/engine/clients/os/blazar.py b/heat/engine/clients/os/blazar.py
index 24429d442..9d1d650bf 100644
--- a/heat/engine/clients/os/blazar.py
+++ b/heat/engine/clients/os/blazar.py
@@ -15,7 +15,9 @@ from blazarclient import client as blazar_client
from blazarclient import exception as client_exception
from oslo_config import cfg
+from heat.common import exception
from heat.engine.clients import client_plugin
+from heat.engine import constraints
CLIENT_NAME = 'blazar'
@@ -58,3 +60,16 @@ class BlazarClientPlugin(client_plugin.ClientPlugin):
def get_host(self, id):
return self.client().host.get(id)
+
+
+class BlazarBaseConstraint(constraints.BaseCustomConstraint):
+
+ resource_client_name = CLIENT_NAME
+
+
+class ReservationConstraint(BlazarBaseConstraint):
+ expected_exceptions = (
+ exception.EntityNotFound,
+ client_exception.BlazarClientException,)
+
+ resource_getter_name = 'get_lease'
diff --git a/heat/engine/clients/os/designate.py b/heat/engine/clients/os/designate.py
index be0f81ee6..9a2b25a2f 100644
--- a/heat/engine/clients/os/designate.py
+++ b/heat/engine/clients/os/designate.py
@@ -39,7 +39,7 @@ class DesignateClientPlugin(client_plugin.ClientPlugin):
return isinstance(ex, exceptions.NotFound)
def get_zone_id(self, zone_id_or_name):
- client = self.client(version=self.V2)
+ client = self.client(version='2')
try:
zone_obj = client.zones.get(zone_id_or_name)
return zone_obj['id']
diff --git a/heat/engine/clients/os/nova.py b/heat/engine/clients/os/nova.py
index 95a9923b7..d8cd22a6f 100644
--- a/heat/engine/clients/os/nova.py
+++ b/heat/engine/clients/os/nova.py
@@ -82,11 +82,9 @@ class NovaClientPlugin(microversion_mixin.MicroversionMixin,
def _get_args(self, version):
endpoint_type = self._get_client_option(CLIENT_NAME, 'endpoint_type')
- extensions = nc.discover_extensions(version)
return {
'session': self.context.keystone_session,
- 'extensions': extensions,
'endpoint_type': endpoint_type,
'service_type': self.COMPUTE,
'region_name': self._get_region_name(),
@@ -448,10 +446,12 @@ echo -e '%s\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers
payload = jsonutils.loads(userdata)
encoded_metadata = urlparse.quote(jsonutils.dumps(metadata))
- cfn_init_data = {
+ path_list = ["/var/lib/heat-cfntools/cfn-init-data",
+ "/var/lib/cloud/data/cfn-init-data"]
+ ignition_format_metadata = {
"filesystem": "root",
"group": {"name": "root"},
- "path": "/var/lib/os-collect-config/local-data",
+ "path": "",
"user": {"name": "root"},
"contents": {
"source": "data:," + encoded_metadata,
@@ -459,16 +459,19 @@ echo -e '%s\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers
"mode": 0o640
}
- storage = payload.setdefault('storage', {})
- try:
- files = storage.setdefault('files', [])
- except AttributeError:
- raise ValueError('Ignition "storage" section must be a map')
- else:
+ for path in path_list:
+ storage = payload.setdefault('storage', {})
try:
- files.append(cfn_init_data)
+ files = storage.setdefault('files', [])
except AttributeError:
- raise ValueError('Ignition "files" section must be a list')
+ raise ValueError('Ignition "storage" section must be a map')
+ else:
+ try:
+ data = ignition_format_metadata.copy()
+ data["path"] = path
+ files.append(data)
+ except AttributeError:
+ raise ValueError('Ignition "files" section must be a list')
return jsonutils.dumps(payload)
@@ -824,15 +827,6 @@ echo -e '%s\tALL=(ALL)\tNOPASSWD: ALL' >> /etc/sudoers
return True
return False
- @os_client.MEMOIZE_EXTENSIONS
- def _list_extensions(self):
- extensions = self.client().list_extensions.show_all()
- return set(extension.alias for extension in extensions)
-
- def has_extension(self, alias):
- """Check if specific extension is present."""
- return alias in self._list_extensions()
-
class NovaBaseConstraint(constraints.BaseCustomConstraint):
diff --git a/heat/engine/resource.py b/heat/engine/resource.py
index c383f0abf..0d08d9569 100644
--- a/heat/engine/resource.py
+++ b/heat/engine/resource.py
@@ -1456,6 +1456,23 @@ class Resource(status.ResourceStatus):
new_requires=new_requires)
runner(timeout=timeout, progress_callback=progress_callback)
+ def handle_preempt(self):
+ """Pre-empt an in-progress update when a new update is available.
+
+ This method is called when a previous convergence update is in
+ progress but a new update for the resource is available. By default
+ it does nothing, but subclasses may override it to cancel the
+ in-progress update if it is safe to do so.
+
+ Note that this method does not run in the context of the in-progress
+ update and has no access to runtime information about it; nor is it
+ safe to make changes to the Resource in the database. If implemented,
+ this method should cause the existing update to complete by external
+ means. If this leaves the resource in a FAILED state, that should be
+ taken into account in needs_replace_failed().
+ """
+ return
+
def preview_update(self, after, before, after_props, before_props,
prev_resource, check_init_complete=False):
"""Simulates update without actually updating the resource.
diff --git a/heat/engine/resources/openstack/neutron/firewall.py b/heat/engine/resources/openstack/neutron/firewall.py
index ef869cd40..93c865e76 100644
--- a/heat/engine/resources/openstack/neutron/firewall.py
+++ b/heat/engine/resources/openstack/neutron/firewall.py
@@ -243,8 +243,8 @@ class FirewallPolicy(neutron.NeutronResource):
),
FIREWALL_RULES: properties.Schema(
properties.Schema.LIST,
- _('An ordered list of firewall rules to apply to the firewall.'),
- required=True,
+ _('An ordered list of firewall rules to apply to the firewall. '
+ '(Prior to version 14.0.0 this was a required property).'),
update_allowed=True
),
}
diff --git a/heat/engine/resources/openstack/nova/flavor.py b/heat/engine/resources/openstack/nova/flavor.py
index 28b1c5f64..afefcbe51 100644
--- a/heat/engine/resources/openstack/nova/flavor.py
+++ b/heat/engine/resources/openstack/nova/flavor.py
@@ -41,8 +41,6 @@ class NovaFlavor(resource.Resource):
default_client_name = 'nova'
- required_service_extension = 'os-flavor-manage'
-
entity = 'flavors'
PROPERTIES = (
diff --git a/heat/engine/resources/openstack/nova/floatingip.py b/heat/engine/resources/openstack/nova/floatingip.py
index 83e5e7fca..9a3d0a16a 100644
--- a/heat/engine/resources/openstack/nova/floatingip.py
+++ b/heat/engine/resources/openstack/nova/floatingip.py
@@ -49,8 +49,6 @@ class NovaFloatingIp(resource.Resource):
)
)
- required_service_extension = 'os-floating-ips'
-
PROPERTIES = (POOL,) = ('pool',)
ATTRIBUTES = (
diff --git a/heat/engine/resources/openstack/nova/host_aggregate.py b/heat/engine/resources/openstack/nova/host_aggregate.py
index ee57fe27b..5264c8393 100644
--- a/heat/engine/resources/openstack/nova/host_aggregate.py
+++ b/heat/engine/resources/openstack/nova/host_aggregate.py
@@ -38,8 +38,6 @@ class HostAggregate(resource.Resource):
entity = 'aggregates'
- required_service_extension = 'os-aggregates'
-
PROPERTIES = (
NAME, AVAILABILITY_ZONE, HOSTS, METADATA
) = (
diff --git a/heat/engine/resources/openstack/nova/keypair.py b/heat/engine/resources/openstack/nova/keypair.py
index bff7c7ba6..9104c8777 100644
--- a/heat/engine/resources/openstack/nova/keypair.py
+++ b/heat/engine/resources/openstack/nova/keypair.py
@@ -43,8 +43,6 @@ class KeyPair(resource.Resource):
support_status = support.SupportStatus(version='2014.1')
- required_service_extension = 'os-keypairs'
-
PROPERTIES = (
NAME, SAVE_PRIVATE_KEY, PUBLIC_KEY, KEY_TYPE, USER,
) = (
diff --git a/heat/engine/resources/openstack/nova/quota.py b/heat/engine/resources/openstack/nova/quota.py
index 269fcdaa7..8761da97a 100644
--- a/heat/engine/resources/openstack/nova/quota.py
+++ b/heat/engine/resources/openstack/nova/quota.py
@@ -53,8 +53,6 @@ class NovaQuota(resource.Resource):
entity = 'quotas'
- required_service_extension = 'os-quota-sets'
-
PROPERTIES = (
PROJECT, CORES, FIXED_IPS, FLOATING_IPS, INSTANCES,
INJECTED_FILES, INJECTED_FILE_CONTENT_BYTES, INJECTED_FILE_PATH_BYTES,
@@ -117,6 +115,14 @@ class NovaQuota(resource.Resource):
properties.Schema.INTEGER,
_('Quota for the number of injected files. '
'Setting the value to -1 removes the limit.'),
+ support_status=support.SupportStatus(
+ status=support.DEPRECATED,
+ version='14.0.0',
+ message=_('File injection is deprecated '
+ 'from compute REST API '
+ 'OS::Nova::Quota resource will not support '
+ 'it in the future.')
+ ),
constraints=[
constraints.Range(min=-1),
],
@@ -126,6 +132,14 @@ class NovaQuota(resource.Resource):
properties.Schema.INTEGER,
_('Quota for the number of injected file content bytes. '
'Setting the value to -1 removes the limit.'),
+ support_status=support.SupportStatus(
+ status=support.DEPRECATED,
+ version='14.0.0',
+ message=_('File injection is deprecated '
+ 'from compute REST API '
+ 'OS::Nova::Quota resource will not support '
+ 'it in the future.')
+ ),
constraints=[
constraints.Range(min=-1),
],
@@ -135,6 +149,14 @@ class NovaQuota(resource.Resource):
properties.Schema.INTEGER,
_('Quota for the number of injected file path bytes. '
'Setting the value to -1 removes the limit.'),
+ support_status=support.SupportStatus(
+ status=support.DEPRECATED,
+ version='14.0.0',
+ message=_('File injection is deprecated '
+ 'from compute REST API '
+ 'OS::Nova::Quota resource will not support '
+ 'it in the future.')
+ ),
constraints=[
constraints.Range(min=-1),
],
diff --git a/heat/engine/resources/openstack/nova/server_group.py b/heat/engine/resources/openstack/nova/server_group.py
index 5b5a4c3ff..abaa8c6b7 100644
--- a/heat/engine/resources/openstack/nova/server_group.py
+++ b/heat/engine/resources/openstack/nova/server_group.py
@@ -33,8 +33,6 @@ class ServerGroup(resource.Resource):
entity = 'server_groups'
- required_service_extension = 'os-server-groups'
-
PROPERTIES = (
NAME, POLICIES
) = (
diff --git a/heat/engine/resources/openstack/nova/server_network_mixin.py b/heat/engine/resources/openstack/nova/server_network_mixin.py
index 6679710e5..c03514262 100644
--- a/heat/engine/resources/openstack/nova/server_network_mixin.py
+++ b/heat/engine/resources/openstack/nova/server_network_mixin.py
@@ -190,12 +190,6 @@ class ServerNetworkMixin(object):
creating. We need to store information about that ports, so store
their IDs to data with key `external_ports`.
"""
- # check if os-attach-interfaces extension is available on this cloud.
- # If it's not, then novaclient's interface_list method cannot be used
- # to get the list of interfaces.
- if not self.client_plugin().has_extension('os-attach-interfaces'):
- return
-
server = self.client().servers.get(self.resource_id)
ifaces = server.interface_list()
external_port_ids = set(iface.port_id for iface in ifaces)
diff --git a/heat/engine/resources/stack_resource.py b/heat/engine/resources/stack_resource.py
index 7def946c5..bb020c06b 100644
--- a/heat/engine/resources/stack_resource.py
+++ b/heat/engine/resources/stack_resource.py
@@ -561,9 +561,10 @@ class StackResource(resource.Resource):
return self._check_status_complete(target_action,
cookie=cookie)
- def handle_update_cancel(self, cookie):
+ def _handle_cancel(self):
stack_identity = self.nested_identifier()
if stack_identity is not None:
+ LOG.debug('Cancelling %s of %s' % (self.action, self))
try:
self.rpc_client().stack_cancel_update(
self.context,
@@ -573,6 +574,12 @@ class StackResource(resource.Resource):
LOG.debug('Nested stack %s not in cancellable state',
stack_identity.stack_name)
+ def handle_preempt(self):
+ self._handle_cancel()
+
+ def handle_update_cancel(self, cookie):
+ self._handle_cancel()
+
def handle_create_cancel(self, cookie):
return self.handle_update_cancel(cookie)
diff --git a/heat/engine/service.py b/heat/engine/service.py
index 62af50a95..5c02798d0 100644
--- a/heat/engine/service.py
+++ b/heat/engine/service.py
@@ -2320,18 +2320,12 @@ class EngineService(service.ServiceBase):
try:
for st in stacks:
lock = stack_lock.StackLock(ctxt, st.id, self.engine_id)
- lock.acquire()
locks.append(lock)
- sess = ctxt.session
- sess.begin(subtransactions=True)
- try:
+ lock.acquire()
+ with ctxt.session.begin():
for st in stacks:
if not st.convergence:
st.migrate_to_convergence()
- sess.commit()
- except Exception:
- sess.rollback()
- raise
finally:
for lock in locks:
lock.release()
diff --git a/heat/engine/worker.py b/heat/engine/worker.py
index 274dd436f..ea8553d75 100644
--- a/heat/engine/worker.py
+++ b/heat/engine/worker.py
@@ -110,6 +110,9 @@ class WorkerService(object):
Marks the stack as FAILED due to cancellation, but, allows all
in_progress resources to complete normally; no worker is stopped
abruptly.
+
+ Any in-progress traversals are also stopped on all nested stacks that
+ are descendants of the one passed.
"""
_stop_traversal(stack)
diff --git a/heat/tests/clients/test_nova_client.py b/heat/tests/clients/test_nova_client.py
index 4af266f36..afd3caaf6 100644
--- a/heat/tests/clients/test_nova_client.py
+++ b/heat/tests/clients/test_nova_client.py
@@ -45,27 +45,21 @@ class NovaClientPluginTest(NovaClientPluginTestCase):
def test_create(self):
context = utils.dummy_context()
- ext_mock = self.patchobject(nc, 'discover_extensions')
plugin = context.clients.client_plugin('nova')
plugin.max_microversion = '2.53'
client = plugin.client()
- ext_mock.assert_called_once_with('2.53')
self.assertIsNotNone(client.servers)
def test_v2_26_create(self):
ctxt = utils.dummy_context()
- ext_mock = self.patchobject(nc, 'discover_extensions')
self.patchobject(nc, 'Client', return_value=mock.Mock())
plugin = ctxt.clients.client_plugin('nova')
plugin.max_microversion = '2.53'
plugin.client(version='2.26')
- ext_mock.assert_called_once_with('2.26')
-
def test_v2_26_create_failed(self):
ctxt = utils.dummy_context()
- self.patchobject(nc, 'discover_extensions')
plugin = ctxt.clients.client_plugin('nova')
plugin.max_microversion = '2.23'
client_stub = mock.Mock()
@@ -402,8 +396,10 @@ class NovaClientPluginUserdataTest(NovaClientPluginTestCase):
userdata=userdata,
user_data_format=ud_format)
ig = json.loads(data)
- self.assertEqual("/var/lib/os-collect-config/local-data",
+ self.assertEqual("/var/lib/heat-cfntools/cfn-init-data",
ig["storage"]["files"][0]["path"])
+ self.assertEqual("/var/lib/cloud/data/cfn-init-data",
+ ig["storage"]["files"][1]["path"])
self.assertEqual("data:,%7B%22os-collect-config%22%3A%20%7B%22heat"
"%22%3A%20%7B%22password%22%3A%20%22%2A%2A%2A%22"
"%7D%7D%7D",
@@ -652,30 +648,3 @@ class ConsoleUrlsTest(common.HeatTestCase):
self.console_method.side_effect = exc("spam")
self._test_get_console_url_tolerate_exception('spam')
-
-
-class NovaClientPluginExtensionsTest(NovaClientPluginTestCase):
- """Tests for extensions in novaclient."""
-
- def test_has_no_extensions(self):
- self.nova_client.list_extensions.show_all.return_value = []
- self.assertFalse(self.nova_plugin.has_extension(
- "os-virtual-interfaces"))
-
- def test_has_no_interface_extensions(self):
- mock_extension = mock.Mock()
- p = mock.PropertyMock(return_value='os-xxxx')
- type(mock_extension).alias = p
- self.nova_client.list_extensions.show_all.return_value = [
- mock_extension]
- self.assertFalse(self.nova_plugin.has_extension(
- "os-virtual-interfaces"))
-
- def test_has_os_interface_extension(self):
- mock_extension = mock.Mock()
- p = mock.PropertyMock(return_value='os-virtual-interfaces')
- type(mock_extension).alias = p
- self.nova_client.list_extensions.show_all.return_value = [
- mock_extension]
- self.assertTrue(self.nova_plugin.has_extension(
- "os-virtual-interfaces"))
diff --git a/heat/tests/engine/test_engine_worker.py b/heat/tests/engine/test_engine_worker.py
index affb51186..bdde6c8be 100644
--- a/heat/tests/engine/test_engine_worker.py
+++ b/heat/tests/engine/test_engine_worker.py
@@ -197,6 +197,29 @@ class WorkerServiceTest(common.HeatTestCase):
self.assertEqual('stack1', call_args1.name)
self.assertEqual('stack2', call_args2.name)
+ @mock.patch.object(worker, '_stop_traversal')
+ def test_stop_nested_traversal_stops_deeply_nested_stack(self, mock_st):
+ mock_tgm = mock.Mock()
+ ctx = utils.dummy_context()
+ tmpl = templatem.Template.create_empty_template()
+ stack1 = parser.Stack(ctx, 'stack1', tmpl,
+ current_traversal='123')
+ stack1.store()
+ stack2 = parser.Stack(ctx, 'stack2', tmpl,
+ owner_id=stack1.id, current_traversal='456')
+ stack2.store()
+ stack3 = parser.Stack(ctx, 'stack3', tmpl,
+ owner_id=stack2.id, current_traversal='789')
+ stack3.store()
+ _worker = worker.WorkerService('host-1', 'topic-1', 'engine-001',
+ mock_tgm)
+ _worker.stop_traversal(stack2)
+ self.assertEqual(2, mock_st.call_count)
+ call1, call2 = mock_st.call_args_list
+ call_args1, call_args2 = call1[0][0], call2[0][0]
+ self.assertEqual('stack2', call_args1.name)
+ self.assertEqual('stack3', call_args2.name)
+
@mock.patch.object(worker, '_cancel_workers')
@mock.patch.object(worker.WorkerService, 'stop_traversal')
def test_stop_all_workers_when_stack_in_progress(self, mock_st, mock_cw):
diff --git a/heat/tests/openstack/nova/fakes.py b/heat/tests/openstack/nova/fakes.py
index 2651a96df..4404ea798 100644
--- a/heat/tests/openstack/nova/fakes.py
+++ b/heat/tests/openstack/nova/fakes.py
@@ -364,6 +364,19 @@ class FakeSessionClient(base_client.SessionClient):
'OS-FLV-EXT-DATA:ephemeral': 30}})
#
+ # Interfaces
+ #
+
+ def get_servers_5678_os_interface(self, **kw):
+ return (200, {'interfaceAttachments':
+ [{"fixed_ips":
+ [{"ip_address": "10.0.0.1",
+ "subnet_id": "f8a6e8f8-c2ec-497c-9f23-da9616de54ef"
+ }],
+ "port_id": "ce531f90-199f-48c0-816c-13e38010b442"
+ }]})
+
+ #
# Floating ips
#
diff --git a/heat/tests/openstack/nova/test_flavor.py b/heat/tests/openstack/nova/test_flavor.py
index 7799fbdbb..1f6afcfdf 100644
--- a/heat/tests/openstack/nova/test_flavor.py
+++ b/heat/tests/openstack/nova/test_flavor.py
@@ -13,7 +13,6 @@
import mock
-from heat.engine.clients.os import nova as novac
from heat.engine import stack
from heat.engine import template
from heat.tests import common
@@ -42,8 +41,6 @@ flavor_template = {
class NovaFlavorTest(common.HeatTestCase):
def setUp(self):
super(NovaFlavorTest, self).setUp()
- self.patchobject(novac.NovaClientPlugin, 'has_extension',
- return_value=True)
self.ctx = utils.dummy_context()
def create_flavor(self, with_name_id=False, is_public=True):
diff --git a/heat/tests/openstack/nova/test_host_aggregate.py b/heat/tests/openstack/nova/test_host_aggregate.py
index 8b164e4e7..0c63fec18 100644
--- a/heat/tests/openstack/nova/test_host_aggregate.py
+++ b/heat/tests/openstack/nova/test_host_aggregate.py
@@ -39,9 +39,6 @@ AGGREGATE_TEMPLATE = {
class NovaHostAggregateTest(common.HeatTestCase):
def setUp(self):
super(NovaHostAggregateTest, self).setUp()
- self.patchobject(nova.NovaClientPlugin,
- 'has_extension',
- return_value=True)
self.ctx = utils.dummy_context()
self.stack = stack.Stack(
diff --git a/heat/tests/openstack/nova/test_keypair.py b/heat/tests/openstack/nova/test_keypair.py
index cc412bbd2..1244bc4f3 100644
--- a/heat/tests/openstack/nova/test_keypair.py
+++ b/heat/tests/openstack/nova/test_keypair.py
@@ -46,8 +46,6 @@ class NovaKeyPairTest(common.HeatTestCase):
self.fake_nova = mock.MagicMock()
self.fake_keypairs = mock.MagicMock()
self.fake_nova.keypairs = self.fake_keypairs
- self.patchobject(nova.NovaClientPlugin, 'has_extension',
- return_value=True)
self.cp_mock = self.patchobject(nova.NovaClientPlugin, 'client',
return_value=self.fake_nova)
diff --git a/heat/tests/openstack/nova/test_quota.py b/heat/tests/openstack/nova/test_quota.py
index 3c813a56b..648db3895 100644
--- a/heat/tests/openstack/nova/test_quota.py
+++ b/heat/tests/openstack/nova/test_quota.py
@@ -16,7 +16,6 @@ import six
from heat.common import exception
from heat.common import template_format
from heat.engine.clients.os import keystone as k_plugin
-from heat.engine.clients.os import nova as n_plugin
from heat.engine import rsrc_defn
from heat.engine import stack as parser
from heat.engine import template
@@ -62,8 +61,6 @@ class NovaQuotaTest(common.HeatTestCase):
super(NovaQuotaTest, self).setUp()
self.ctx = utils.dummy_context()
- self.patchobject(n_plugin.NovaClientPlugin, 'has_extension',
- return_value=True)
self.patchobject(k_plugin.KeystoneClientPlugin, 'get_project_id',
return_value='some_project_id')
tpl = template_format.parse(quota_template)
diff --git a/heat/tests/openstack/nova/test_server.py b/heat/tests/openstack/nova/test_server.py
index 0f6858d0c..8744466d7 100644
--- a/heat/tests/openstack/nova/test_server.py
+++ b/heat/tests/openstack/nova/test_server.py
@@ -1349,8 +1349,6 @@ class ServersTest(common.HeatTestCase):
}
}
'''
- self.patchobject(nova.NovaClientPlugin, 'has_extension',
- return_value=True)
t = template_format.parse(nova_keypair_template)
templ = template.Template(t)
self.patchobject(nova.NovaClientPlugin, 'client',
@@ -4947,7 +4945,6 @@ class ServerInternalPortTest(ServersTest):
server.client = mock.Mock()
server.client().servers.get.return_value = Fake()
server.client_plugin = mock.Mock()
- server.client_plugin().has_extension.return_value = True
server._data = {"internal_ports": '[{"id": "1122"}]',
"external_ports": '[{"id": "3344"},{"id": "5566"}]'}
@@ -5220,28 +5217,3 @@ class ServerInternalPortTest(ServersTest):
mock.call('prev_rsrc', 1122),
mock.call('prev_rsrc', 3344),
mock.call('prev_rsrc', 5566)])
-
- def test_store_external_ports_os_interface_not_installed(self):
- t, stack, server = self._return_template_stack_and_rsrc_defn(
- 'test', tmpl_server_with_network_id)
-
- class Fake(object):
- def interface_list(self):
- return [iface('1122'),
- iface('1122'),
- iface('2233'),
- iface('3344')]
-
- server.client = mock.Mock()
- server.client().servers.get.return_value = Fake()
- server.client_plugin = mock.Mock()
- server.client_plugin().has_extension.return_value = False
-
- server._data = {"internal_ports": '[{"id": "1122"}]',
- "external_ports": '[{"id": "3344"},{"id": "5566"}]'}
-
- iface = collections.namedtuple('iface', ['port_id'])
- update_data = self.patchobject(server, '_data_update_ports')
-
- server.store_external_ports()
- self.assertEqual(0, update_data.call_count)
diff --git a/heat/tests/openstack/nova/test_server_group.py b/heat/tests/openstack/nova/test_server_group.py
index a8129fa9e..6a2e8bed5 100644
--- a/heat/tests/openstack/nova/test_server_group.py
+++ b/heat/tests/openstack/nova/test_server_group.py
@@ -16,7 +16,6 @@ import json
import mock
from heat.common import template_format
-from heat.engine.clients.os import nova
from heat.engine import scheduler
from heat.tests import common
from heat.tests import utils
@@ -44,8 +43,6 @@ class FakeGroup(object):
class NovaServerGroupTest(common.HeatTestCase):
def setUp(self):
super(NovaServerGroupTest, self).setUp()
- self.patchobject(nova.NovaClientPlugin, 'has_extension',
- return_value=True)
def _init_template(self, sg_template):
template = template_format.parse(json.dumps(sg_template))