diff options
Diffstat (limited to 'heat/engine')
-rw-r--r-- | heat/engine/event.py | 63 | ||||
-rw-r--r-- | heat/engine/resource.py | 38 | ||||
-rw-r--r-- | heat/engine/stack.py | 2 |
3 files changed, 40 insertions, 63 deletions
diff --git a/heat/engine/event.py b/heat/engine/event.py index c7865f734..498427ad8 100644 --- a/heat/engine/event.py +++ b/heat/engine/event.py @@ -11,19 +11,9 @@ # License for the specific language governing permissions and limitations # under the License. -import six - -from sqlalchemy.util.compat import pickle - -import oslo_db.exception -from oslo_log import log as logging - from heat.common import identifier from heat.objects import event as event_object - -LOG = logging.getLogger(__name__) - -MAX_EVENT_RESOURCE_PROPERTIES_SIZE = (1 << 16) - 1 +from heat.objects import resource_properties_data as rpd_objects class Event(object): @@ -45,10 +35,17 @@ class Event(object): self.physical_resource_id = physical_resource_id self.resource_name = resource_name self.resource_type = resource_type - try: - self.resource_properties = dict(resource_properties) - except ValueError as ex: - self.resource_properties = {'Error': six.text_type(ex)} + self.rsrc_prop_data = None + if isinstance(resource_properties, + rpd_objects.ResourcePropertiesData): + self.rsrc_prop_data = resource_properties + self.resource_properties = self.rsrc_prop_data.data + elif resource_properties is None: + self.resource_properties = {} + else: + raise AssertionError( + _('resource_properties is unexpected type %s'), + type(resource_properties)) self.uuid = uuid self.timestamp = timestamp self.id = id @@ -63,7 +60,6 @@ class Event(object): 'resource_status': self.status, 'resource_status_reason': self.reason, 'resource_type': self.resource_type, - 'resource_properties': self.resource_properties, } if self.uuid is not None: @@ -72,39 +68,10 @@ class Event(object): if self.timestamp is not None: ev['created_at'] = self.timestamp - # Workaround: we don't want to attempt to store the - # event.resource_properties column if the data is too large - # (greater than permitted by BLOB). Otherwise, we end up with - # an unsightly log message. - rp_size = len(pickle.dumps(ev['resource_properties'], - pickle.HIGHEST_PROTOCOL)) - if rp_size > MAX_EVENT_RESOURCE_PROPERTIES_SIZE: - LOG.debug('event\'s resource_properties too large to store at ' - '%d bytes', rp_size) - # Try truncating the largest value and see if that gets us under - # the db column's size constraint. - max_key, max_val = max(ev['resource_properties'].items(), - key=lambda i: len(repr(i[1]))) - err = 'Resource properties are too large to store fully' - ev['resource_properties'].update({'Error': err}) - ev['resource_properties'][max_key] = '<Deleted, too large>' - rp_size = len(pickle.dumps(ev['resource_properties'], - pickle.HIGHEST_PROTOCOL)) - if rp_size > MAX_EVENT_RESOURCE_PROPERTIES_SIZE: - LOG.debug('event\'s resource_properties STILL too large ' - 'after truncating largest key at %d bytes', rp_size) - err = 'Resource properties are too large to attempt to store' - ev['resource_properties'] = {'Error': err} + if self.rsrc_prop_data: + ev['rsrc_prop_data_id'] = self.rsrc_prop_data.id - # We should have worked around the issue, but let's be extra - # careful. - try: - new_ev = event_object.Event.create(self.context, ev) - except oslo_db.exception.DBError: - # Give up and drop all properties.. - err = 'Resource properties are too large to store' - ev['resource_properties'] = {'Error': err} - new_ev = event_object.Event.create(self.context, ev) + new_ev = event_object.Event.create(self.context, ev) self.id = new_ev.id self.timestamp = new_ev.created_at diff --git a/heat/engine/resource.py b/heat/engine/resource.py index a5b369bb2..3326c5ed3 100644 --- a/heat/engine/resource.py +++ b/heat/engine/resource.py @@ -44,6 +44,7 @@ from heat.engine import scheduler from heat.engine import support from heat.objects import resource as resource_objects from heat.objects import resource_data as resource_data_objects +from heat.objects import resource_properties_data as rpd_objects from heat.objects import stack as stack_objects from heat.rpc import client as rpc_client @@ -249,6 +250,7 @@ class Resource(object): self.uuid = None self._data = None self._rsrc_metadata = None + self._rsrc_prop_data = None self._stored_properties_data = None self.created_time = stack.created_time self.updated_time = stack.updated_time @@ -291,6 +293,7 @@ class Resource(object): self._data = {} self._rsrc_metadata = resource.rsrc_metadata self._stored_properties_data = resource.properties_data + self._rsrc_prop_data = resource.rsrc_prop_data self.created_time = resource.created_at self.updated_time = resource.updated_at self.needed_by = resource.needed_by @@ -350,7 +353,7 @@ class Resource(object): # Don't set physical_resource_id so that a create is triggered. rs = {'stack_id': self.stack.id, 'name': self.name, - 'properties_data': self._stored_properties_data, + 'rsrc_prop_data_id': self._create_or_replace_rsrc_prop_data(), 'needed_by': self.needed_by, 'requires': self.requires, 'replaces': self.id, @@ -858,7 +861,10 @@ class Resource(object): yield self.action_handler_task(action, args=handler_args) def _update_stored_properties(self): + old_props = self._stored_properties_data self._stored_properties_data = function.resolve(self.properties.data) + if self._stored_properties_data != old_props: + self._rsrc_prop_data = None def preview(self): """Default implementation of Resource.preview. @@ -1721,10 +1727,6 @@ class Resource(object): def _store(self, metadata=None): """Create the resource in the database.""" - - properties_data_encrypted, properties_data = ( - resource_objects.Resource.encrypt_properties_data( - self._stored_properties_data)) if not self.root_stack_id: self.root_stack_id = self.stack.root_stack_id() try: @@ -1735,8 +1737,8 @@ class Resource(object): 'physical_resource_id': self.resource_id, 'name': self.name, 'rsrc_metadata': metadata, - 'properties_data': properties_data, - 'properties_data_encrypted': properties_data_encrypted, + 'rsrc_prop_data_id': + self._create_or_replace_rsrc_prop_data(), 'needed_by': self.needed_by, 'requires': self.requires, 'replaces': self.replaces, @@ -1744,7 +1746,6 @@ class Resource(object): 'current_template_id': self.current_template_id, 'stack_name': self.stack.name, 'root_stack_id': self.root_stack_id} - new_rs = resource_objects.Resource.create(self.context, rs) self.id = new_rs.id self.uuid = new_rs.uuid @@ -1757,7 +1758,7 @@ class Resource(object): """Add a state change event to the database.""" physical_res_id = self.resource_id or self.physical_resource_name() ev = event.Event(self.context, self.stack, action, status, reason, - physical_res_id, self.properties, + physical_res_id, self._rsrc_prop_data, self.name, self.type()) ev.store() @@ -1769,18 +1770,15 @@ class Resource(object): self.status = status self.status_reason = reason - properties_data_encrypted, properties_data = ( - resource_objects.Resource.encrypt_properties_data( - self._stored_properties_data)) data = { 'action': self.action, 'status': self.status, 'status_reason': reason, 'stack_id': self.stack.id, 'updated_at': self.updated_time, - 'properties_data': properties_data, - 'properties_data_encrypted': properties_data_encrypted, 'needed_by': self.needed_by, + 'rsrc_prop_data_id': self._create_or_replace_rsrc_prop_data(), + 'properties_data': None, 'requires': self.requires, 'replaces': self.replaces, 'replaced_by': self.replaced_by, @@ -2286,6 +2284,18 @@ class Resource(object): self._data = None return True + def _create_or_replace_rsrc_prop_data(self): + if self._rsrc_prop_data is not None: + return self._rsrc_prop_data.id + + if not self._stored_properties_data: + return None + + self._rsrc_prop_data = \ + rpd_objects.ResourcePropertiesData(self.context).create( + self.context, self._stored_properties_data) + return self._rsrc_prop_data.id + def is_using_neutron(self): try: sess_client = self.client('neutron').httpclient diff --git a/heat/engine/stack.py b/heat/engine/stack.py index e6bcea5d4..bc3429f34 100644 --- a/heat/engine/stack.py +++ b/heat/engine/stack.py @@ -887,7 +887,7 @@ class Stack(collections.Mapping): def _add_event(self, action, status, reason): """Add a state change event to the database.""" ev = event.Event(self.context, self, action, status, reason, - self.id, {}, + self.id, None, self.name, 'OS::Heat::Stack') ev.store() |