summaryrefslogtreecommitdiff
path: root/heat/engine/template_files.py
blob: bcbe68b07a0c4be13e873a8bd77df06dcb708553 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#
#    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 weakref

from heat.common import context
from heat.common import exception
from heat.common.i18n import _
from heat.db 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.abc.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, int):
            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