7c389dd2a5
in heat/contrib/rackspace/rackspace/tests/test_auto_scale.py:270: mock nova and glance client methods to satisfy contraints, contraints should be constraints in heat/heat_integrationtests/functional/test_resource_group.py:51 triggering validation of nested resource custom contraints, contraints should be constraints in heat/heat/common/exception.py:258: """Keep this for AWS compatiblility.""" compatiblility should be compatibility in heat/heat/engine/resources/openstack/ceilometer/alarm.py:349: 1) so we don't create watch tasks unneccessarly , unneccessarly should be unnecessarily in heat/heat/engine/resources/openstack/neutron/vpnservice.py:462: The Internet Key Exchange policy identifyies the authentication and , identifyies should be identifies in heat/heat/engine/resources/openstack/nova/server.py:1426: if 'security_groups' present for the server and explict 'port' , explict should be explicit in heat/heat/engine/service.py:182: releasing the lock to avoid race condtitions. condtitions should be conditions in heat/heat/engine/sync_point.py:134: don't aggresively spin; induce some sleep, aggresively should be aggressively in heat/heat/tests/openstack/heat/test_software_deployment.py:889: Test bug 1332355, where details contains a translateable message, translateable should be translatable in heat/heat/tests/test_environment.py:596: make sure the parent env is uneffected, uneffected should be unaffected in heat/heat/engine/resources/openstack/nova/server.py:472: 'ignorning it or by replacing the entire server.'), ignorning should be ignoring in heat/contrib/rackspace/rackspace/resources/cloud_server.py:104: 'retained for compatability.'), compatability should be compatibility in heat/heat/engine/stack.py:1258: " ID %(trvsl_id)s, not trigerring rollback."), trigerring should be triggering. Change-Id: Ic4ddb65dbfaf61751a330b853780689209f9f4b5 Closes-Bug: #1595376
147 lines
4.4 KiB
Python
147 lines
4.4 KiB
Python
#
|
|
# 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.
|
|
|
|
import ast
|
|
import eventlet
|
|
import random
|
|
import six
|
|
|
|
from oslo_log import log as logging
|
|
|
|
from heat.common import exception
|
|
from heat.objects import sync_point as sync_point_object
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
|
|
|
|
KEY_SEPERATOR = ':'
|
|
|
|
|
|
def _dump_list(items, separator=', '):
|
|
return separator.join(map(str, items))
|
|
|
|
|
|
def make_key(*components):
|
|
assert len(components) >= 2
|
|
return _dump_list(components, KEY_SEPERATOR)
|
|
|
|
|
|
def create(context, entity_id, traversal_id, is_update, stack_id):
|
|
"""Creates a sync point entry in DB."""
|
|
values = {'entity_id': entity_id, 'traversal_id': traversal_id,
|
|
'is_update': is_update, 'atomic_key': 0,
|
|
'stack_id': stack_id, 'input_data': {}}
|
|
return sync_point_object.SyncPoint.create(context, values)
|
|
|
|
|
|
def get(context, entity_id, traversal_id, is_update):
|
|
"""Retrieves a sync point entry from DB."""
|
|
sync_point = sync_point_object.SyncPoint.get_by_key(context, entity_id,
|
|
traversal_id,
|
|
is_update)
|
|
if sync_point is None:
|
|
key = (entity_id, traversal_id, is_update)
|
|
raise exception.EntityNotFound(entity='Sync Point', name=key)
|
|
|
|
return sync_point
|
|
|
|
|
|
def delete_all(context, stack_id, traversal_id):
|
|
"""Deletes all sync points of a stack associated with a traversal_id."""
|
|
return sync_point_object.SyncPoint.delete_all_by_stack_and_traversal(
|
|
context, stack_id, traversal_id
|
|
)
|
|
|
|
|
|
def update_input_data(context, entity_id, current_traversal,
|
|
is_update, atomic_key, input_data):
|
|
rows_updated = sync_point_object.SyncPoint.update_input_data(
|
|
context, entity_id, current_traversal, is_update, atomic_key,
|
|
input_data)
|
|
|
|
return rows_updated
|
|
|
|
|
|
def _str_pack_tuple(t):
|
|
return u'tuple:' + str(t)
|
|
|
|
|
|
def _str_unpack_tuple(s):
|
|
s = s[s.index(':') + 1:]
|
|
return ast.literal_eval(s)
|
|
|
|
|
|
def _deserialize(d):
|
|
d2 = {}
|
|
for k, v in d.items():
|
|
if isinstance(k, six.string_types) and k.startswith(u'tuple:('):
|
|
k = _str_unpack_tuple(k)
|
|
if isinstance(v, dict):
|
|
v = _deserialize(v)
|
|
d2[k] = v
|
|
return d2
|
|
|
|
|
|
def _serialize(d):
|
|
d2 = {}
|
|
for k, v in d.items():
|
|
if isinstance(k, tuple):
|
|
k = _str_pack_tuple(k)
|
|
if isinstance(v, dict):
|
|
v = _serialize(v)
|
|
d2[k] = v
|
|
return d2
|
|
|
|
|
|
def deserialize_input_data(db_input_data):
|
|
db_input_data = db_input_data.get('input_data')
|
|
if not db_input_data:
|
|
return {}
|
|
|
|
return dict(_deserialize(db_input_data))
|
|
|
|
|
|
def serialize_input_data(input_data):
|
|
return {'input_data': _serialize(input_data)}
|
|
|
|
|
|
def sync(cnxt, entity_id, current_traversal, is_update, propagate,
|
|
predecessors, new_data):
|
|
rows_updated = None
|
|
sync_point = None
|
|
input_data = None
|
|
nconflicts = max(0, len(predecessors) - 2)
|
|
# limit to 10 seconds
|
|
max_wt = min(nconflicts * 0.01, 10)
|
|
while not rows_updated:
|
|
sync_point = get(cnxt, entity_id, current_traversal, is_update)
|
|
input_data = deserialize_input_data(sync_point.input_data)
|
|
input_data.update(new_data)
|
|
rows_updated = update_input_data(
|
|
cnxt, entity_id, current_traversal, is_update,
|
|
sync_point.atomic_key, serialize_input_data(input_data))
|
|
# don't aggressively spin; induce some sleep
|
|
if not rows_updated:
|
|
eventlet.sleep(random.uniform(0, max_wt))
|
|
|
|
waiting = predecessors - set(input_data)
|
|
key = make_key(entity_id, current_traversal, is_update)
|
|
if waiting:
|
|
LOG.debug('[%s] Waiting %s: Got %s; still need %s',
|
|
key, entity_id, _dump_list(input_data), _dump_list(waiting))
|
|
else:
|
|
LOG.debug('[%s] Ready %s: Got %s',
|
|
key, entity_id, _dump_list(input_data))
|
|
propagate(entity_id, serialize_input_data(input_data))
|