3ab4f15a43
This provides an option to specify a swift container for stack actions and all child templates and env files will be fetched from the container, if available. However, files coming in the 'files' map from the client will have precedence, if the same is also present in swift. Change-Id: Ifa21fbcb41fcb77827997cce2d5e9266ba849b17 Story: #1755453 Task: 17353
156 lines
5.2 KiB
Python
156 lines
5.2 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 collections
|
|
import six
|
|
import weakref
|
|
|
|
from heat.common import context
|
|
from heat.common import exception
|
|
from heat.common.i18n import _
|
|
from heat.db.sqlalchemy import api as db_api
|
|
from heat.objects import raw_template_files
|
|
|
|
_d = weakref.WeakValueDictionary()
|
|
|
|
|
|
class ReadOnlyDict(dict):
|
|
def __setitem__(self, key):
|
|
raise ValueError("Attempted to write to internal TemplateFiles cache")
|
|
|
|
|
|
class TemplateFiles(collections.Mapping):
|
|
|
|
def __init__(self, files):
|
|
self.files = None
|
|
self.files_id = None
|
|
if files is None:
|
|
return
|
|
if isinstance(files, TemplateFiles):
|
|
self.files_id = files.files_id
|
|
self.files = files.files
|
|
return
|
|
if isinstance(files, six.integer_types):
|
|
self.files_id = files
|
|
if self.files_id in _d:
|
|
self.files = _d[self.files_id]
|
|
return
|
|
if not isinstance(files, dict):
|
|
raise ValueError(_('Expected dict, got %(cname)s for files, '
|
|
'(value is %(val)s)') %
|
|
{'cname': files.__class__,
|
|
'val': str(files)})
|
|
# the dict has not been persisted as a raw_template_files db obj
|
|
# yet, so no self.files_id
|
|
self.files = ReadOnlyDict(files)
|
|
|
|
def __getitem__(self, key):
|
|
self._refresh_if_needed()
|
|
if self.files is None:
|
|
raise KeyError
|
|
return self.files[key]
|
|
|
|
def __setitem__(self, key, value):
|
|
self.update({key: value})
|
|
|
|
def __len__(self):
|
|
self._refresh_if_needed()
|
|
if not self.files:
|
|
return 0
|
|
return len(self.files)
|
|
|
|
def __contains__(self, key):
|
|
self._refresh_if_needed()
|
|
if not self.files:
|
|
return False
|
|
return key in self.files
|
|
|
|
def __iter__(self):
|
|
self._refresh_if_needed()
|
|
if self.files is None:
|
|
return iter(ReadOnlyDict({}))
|
|
return iter(self.files)
|
|
|
|
def _refresh_if_needed(self):
|
|
# retrieve files from db if needed
|
|
if self.files_id is None:
|
|
return
|
|
if self.files_id in _d:
|
|
self.files = _d[self.files_id]
|
|
return
|
|
self._refresh()
|
|
|
|
def _refresh(self):
|
|
ctxt = context.get_admin_context()
|
|
rtf_obj = db_api.raw_template_files_get(ctxt, self.files_id)
|
|
_files_dict = ReadOnlyDict(rtf_obj.files)
|
|
self.files = _files_dict
|
|
_d[self.files_id] = _files_dict
|
|
|
|
def store(self, ctxt):
|
|
if not self.files or self.files_id is not None:
|
|
# Do not to persist an empty raw_template_files obj. If we
|
|
# already have a not null self.files_id, the (immutable)
|
|
# raw_templated_object has already been persisted so just
|
|
# return the id.
|
|
return self.files_id
|
|
rtf_obj = raw_template_files.RawTemplateFiles.create(
|
|
ctxt, {'files': self.files})
|
|
self.files_id = rtf_obj.id
|
|
_d[self.files_id] = self.files
|
|
return self.files_id
|
|
|
|
def update(self, files):
|
|
# Sets up the next call to store() to create a new
|
|
# raw_template_files db obj. It seems like we *could* just
|
|
# update the existing raw_template_files obj, but the problem
|
|
# with that is other heat-engine processes' _d dictionaries
|
|
# would have stale data for a given raw_template_files.id with
|
|
# no way of knowing whether that data should be refreshed or
|
|
# not. So, just avoid the potential for weird race conditions
|
|
# and create another db obj in the next store().
|
|
if len(files) == 0:
|
|
return
|
|
if not isinstance(files, dict):
|
|
raise ValueError(_('Expected dict, got %(cname)s for files, '
|
|
'(value is %(val)s)') %
|
|
{'cname': files.__class__,
|
|
'val': str(files)})
|
|
|
|
self._refresh_if_needed()
|
|
if self.files:
|
|
new_files = self.files.copy()
|
|
new_files.update(files)
|
|
else:
|
|
new_files = files
|
|
self.files_id = None # not persisted yet
|
|
self.files = ReadOnlyDict(new_files)
|
|
|
|
|
|
def get_files_from_container(cnxt, files_container, files=None):
|
|
|
|
if files is None:
|
|
files = {}
|
|
else:
|
|
files = files.copy()
|
|
|
|
swift_plugin = cnxt.clients.client_plugin('swift')
|
|
|
|
if not swift_plugin:
|
|
raise exception.ClientNotAvailable(client_name='swift')
|
|
|
|
new_files = swift_plugin.get_files_from_container(files_container,
|
|
list(files.keys()))
|
|
new_files.update(files)
|
|
return new_files
|