Merge ~ajkavanagh/ubuntu/+source/heat:master into ~ubuntu-openstack-dev/ubuntu/+source/heat:master

Proposed by James Page
Status: Merged
Merged at revision: ad629122a96c14fb5a219bf914d6503c8824de84
Proposed branch: ~ajkavanagh/ubuntu/+source/heat:master
Merge into: ~ubuntu-openstack-dev/ubuntu/+source/heat:master
Diff against target: 2798 lines (+766/-583)
84 files modified
.pre-commit-config.yaml (+1/-1)
.zuul.yaml (+8/-0)
AUTHORS (+3/-0)
ChangeLog (+25/-0)
HACKING.rst (+4/-4)
PKG-INFO (+85/-84)
config-generator.conf (+3/-0)
debian/changelog (+8/-2)
debian/patches/drop-zun.patch (+3/-3)
debian/patches/series (+0/-1)
debian/patches/sudoers_patch.patch (+1/-1)
dev/null (+0/-63)
devstack/lib/heat (+0/-11)
doc/source/conf.py (+4/-3)
doc/source/operating_guides/upgrades_guide.rst (+1/-1)
doc/source/template_guide/multi-clouds.rst (+1/-1)
heat/api/openstack/v1/util.py (+8/-2)
heat/cloudinit/loguserdata.py (+0/-53)
heat/cloudinit/part_handler.py (+1/-1)
heat/common/cache.py (+56/-47)
heat/common/config.py (+13/-0)
heat/common/plugin_loader.py (+3/-1)
heat/db/api.py (+30/-1)
heat/engine/clients/os/keystone/fake_keystoneclient.py (+1/-1)
heat/engine/clients/os/keystone/heat_keystoneclient.py (+2/-2)
heat/engine/constraint/common_constraints.py (+12/-3)
heat/engine/hot/functions.py (+7/-2)
heat/engine/resource.py (+1/-1)
heat/engine/resources/openstack/monasca/alarm_definition.py (+8/-4)
heat/engine/resources/openstack/monasca/notification.py (+9/-4)
heat/engine/resources/openstack/neutron/port.py (+1/-1)
heat/engine/resources/openstack/neutron/security_group.py (+1/-1)
heat/engine/resources/openstack/sahara/cluster.py (+9/-0)
heat/engine/resources/openstack/sahara/data_source.py (+8/-1)
heat/engine/resources/openstack/sahara/image.py (+8/-1)
heat/engine/resources/openstack/sahara/job.py (+8/-1)
heat/engine/resources/openstack/sahara/job_binary.py (+8/-1)
heat/engine/resources/openstack/sahara/templates.py (+8/-1)
heat/engine/resources/openstack/senlin/res_base.py (+8/-1)
heat/engine/service.py (+35/-1)
heat/engine/service_software_config.py (+7/-0)
heat/engine/stack.py (+3/-2)
heat/hacking/checks.py (+9/-9)
heat/objects/snapshot.py (+7/-2)
heat/objects/software_config.py (+4/-0)
heat/objects/software_deployment.py (+5/-0)
heat/tests/api/cfn/test_api_cfn_v1.py (+4/-4)
heat/tests/api/openstack_v1/test_routes.py (+2/-2)
heat/tests/api/openstack_v1/test_stacks.py (+2/-2)
heat/tests/clients/test_swift_client.py (+12/-2)
heat/tests/constraints/test_common_constraints.py (+10/-1)
heat/tests/convergence/framework/engine_wrapper.py (+4/-4)
heat/tests/db/test_sqlalchemy_api.py (+83/-33)
heat/tests/engine/service/test_software_config.py (+20/-3)
heat/tests/engine/service/test_stack_snapshot.py (+13/-0)
heat/tests/engine/test_resource_type.py (+1/-2)
heat/tests/openstack/aodh/test_alarm.py (+10/-1)
heat/tests/openstack/heat/test_none_resource.py (+1/-1)
heat/tests/openstack/heat/test_remote_stack.py (+1/-1)
heat/tests/openstack/heat/test_software_deployment.py (+1/-1)
heat/tests/openstack/neutron/test_neutron_floating_ip.py (+3/-3)
heat/tests/test_common_context.py (+1/-1)
heat/tests/test_convg_stack.py (+2/-2)
heat/tests/test_environment.py (+2/-5)
heat/tests/test_loguserdata.py (+1/-74)
heat/tests/test_resource.py (+6/-6)
heat/tests/test_stack_delete.py (+4/-4)
heat/tests/test_template_format.py (+5/-5)
openstack_heat.egg-info/PKG-INFO (+85/-84)
openstack_heat.egg-info/SOURCES.txt (+2/-0)
openstack_heat.egg-info/entry_points.txt (+6/-0)
openstack_heat.egg-info/pbr.json (+1/-1)
openstack_heat.egg-info/requires.txt (+6/-1)
releasenotes/config.yaml (+1/-1)
releasenotes/notes/deprecate-inactive-project-rsc-caracal-ba0b0005f67580f9.yaml (+10/-0)
releasenotes/notes/limit-resources-aeb2f24e705840de.yaml (+26/-0)
releasenotes/source/victoria.rst (+1/-1)
releasenotes/source/wallaby.rst (+1/-1)
releasenotes/source/xena.rst (+1/-1)
releasenotes/source/yoga.rst (+1/-1)
requirements.txt (+2/-1)
setup.cfg (+2/-0)
test-requirements.txt (+1/-9)
tox.ini (+5/-6)
Reviewer Review Type Date Requested Status
Ubuntu OpenStack uploaders Pending
Review via email: mp+463034@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
2index 0ae225c..ee085de 100644
3--- a/.pre-commit-config.yaml
4+++ b/.pre-commit-config.yaml
5@@ -22,7 +22,7 @@ repos:
6 - id: flake8
7 name: flake8
8 additional_dependencies:
9- - hacking>=3.1.0,<3.2.0
10+ - hacking>=6.1.0,<6.2.0
11 language: python
12 entry: flake8
13 files: '^.*\.py$'
14diff --git a/.zuul.yaml b/.zuul.yaml
15index 5cc2149..6072137 100644
16--- a/.zuul.yaml
17+++ b/.zuul.yaml
18@@ -188,6 +188,12 @@
19 - ^releasenotes/.*$
20
21 - job:
22+ name: grenade-heat-multinode-skip-level
23+ parent: grenade-heat-multinode
24+ vars:
25+ grenade_from_branch: stable/2023.1
26+
27+- job:
28 name: heat-tox-py310-with-sqlalchemy-2x
29 parent: openstack-tox-py310
30 description: |
31@@ -215,6 +221,7 @@
32 check:
33 jobs:
34 - grenade-heat-multinode
35+ - grenade-heat-multinode-skip-level
36 - heat-functional
37 - heat-functional-legacy
38 - heat-functional-centos-9-stream
39@@ -222,5 +229,6 @@
40 gate:
41 jobs:
42 - grenade-heat-multinode
43+ - grenade-heat-multinode-skip-level
44 - heat-functional
45 - heat-functional-legacy
46diff --git a/AUTHORS b/AUTHORS
47index b95d2a9..4807711 100644
48--- a/AUTHORS
49+++ b/AUTHORS
50@@ -116,6 +116,7 @@ Dave Wilde <david.wilde@rackspace.com>
51 David Hill <dhill@redhat.com>
52 David J Peacock <david.j.peacock@gmail.com>
53 David Rabel <rabel@b1-systems.de>
54+David Vallee Delisle <dvd@redhat.com>
55 Deepak Tiwari <deepak.tiwari@aricent.com>
56 Deliang Fan <fandeliang@letv.com>
57 Denes Nemeth <denes.nemeth@nokia.com>
58@@ -196,6 +197,7 @@ Jaime Guerrero <jg3755@att.com>
59 Jake Yip <jake.yip@ardc.edu.au>
60 James Combs <cornracker@gmail.com>
61 James E. Blair <jeblair@redhat.com>
62+James Page <james.page@canonical.com>
63 James Reeves <james.reeves5546@gmail.com>
64 James Slagle <jslagle@redhat.com>
65 Jamie Lennox <jamielennox@gmail.com>
66@@ -563,6 +565,7 @@ liusheng <liusheng@huawei.com>
67 liyi <liyi8611@gmail.com>
68 liyi <liyi@liyideMacBook-Pro.local>
69 lizheming <lizheming.li@huawei.com>
70+lujiefsi <lujie@ict.ac.cn>
71 lvdongbing <dongbing.lv@kylin-cloud.com>
72 maniksidana019 <manik@voereir.com>
73 matthew-fuller <mfuller@suse.com>
74diff --git a/ChangeLog b/ChangeLog
75index c4eb64a..3b1c682 100644
76--- a/ChangeLog
77+++ b/ChangeLog
78@@ -1,6 +1,31 @@
79 CHANGES
80 =======
81
82+22.0.0.0rc1
83+-----------
84+
85+* Update regex to detect closed branch
86+* Restore tests disabled because of libvirt bug
87+* Fix access by admin users to resources in different projects
88+* reno: Update master for unmaintained/xena
89+* reno: Update master for unmaintained/wallaby
90+* reno: Update master for unmaintained/victoria
91+* Stop using deprecated implicit\_prefix option
92+* reno: Update master for unmaintained/yoga
93+* Add skip level upgrade job
94+* Fix minor typos in documentation
95+* common: Replace deprecated importlib API (2/2)
96+* Remove logic for cloud-init < 0.6.0
97+* Drop unused default password in create\_stack\_user
98+* Bump hacking (again)
99+* Use zoneinfo instead of pytz if available
100+* Allow more options to limit number of resources
101+* Fix inconsistent naming in db api
102+* Stop unnecessary usage of 'self'
103+* tests: Fix unit tests using has\_calls
104+* Deprecate resources for inactive services
105+* Bump hacking
106+* Adding oslo.config entry points for yaql and cache opts
107 * Add recent release names
108 * Drop remaining references to the removed bin scripts
109 * Remove deprecated wrappertask decorator
110diff --git a/HACKING.rst b/HACKING.rst
111index 618cfa3..d16d300 100644
112--- a/HACKING.rst
113+++ b/HACKING.rst
114@@ -50,8 +50,8 @@ done in the OpenStack CI systems.
115 Heat Specific Commandments
116 --------------------------
117
118-- [Heat301] Use LOG.warning() rather than LOG.warn().
119-- [Heat302] Python 3: do not use dict.iteritems.
120-- [Heat303] Python 3: do not use dict.iterkeys.
121-- [Heat304] Python 3: do not use dict.itervalues.
122+- [HE301] Use LOG.warning() rather than LOG.warn().
123+- [HE302] Python 3: do not use dict.iteritems.
124+- [HE303] Python 3: do not use dict.iterkeys.
125+- [HE304] Python 3: do not use dict.itervalues.
126
127diff --git a/PKG-INFO b/PKG-INFO
128index d68f1ed..9541147 100644
129--- a/PKG-INFO
130+++ b/PKG-INFO
131@@ -1,10 +1,93 @@
132-Metadata-Version: 2.1
133+Metadata-Version: 1.2
134 Name: openstack-heat
135-Version: 21.1.0.dev70
136+Version: 22.0.0.0rc1
137 Summary: OpenStack Orchestration
138 Home-page: https://docs.openstack.org/heat/latest/
139 Author: OpenStack
140 Author-email: openstack-discuss@lists.openstack.org
141+License: UNKNOWN
142+Description: ========================
143+ Team and repository tags
144+ ========================
145+
146+ .. image:: https://governance.openstack.org/tc/badges/heat.svg
147+ :target: https://governance.openstack.org/tc/reference/tags/index.html
148+
149+ .. Change things from this point on
150+
151+ ====
152+ Heat
153+ ====
154+
155+ Heat is a service to orchestrate multiple composite cloud applications using
156+ templates, through both an OpenStack-native REST API and a
157+ CloudFormation-compatible Query API.
158+
159+ Why heat? It makes the clouds rise and keeps them there.
160+
161+ Getting Started
162+ ---------------
163+
164+ If you'd like to run from the master branch, you can clone the git repo:
165+
166+ git clone https://opendev.org/openstack/heat
167+
168+
169+ * Documentation: https://docs.openstack.org/heat/latest
170+ * Template samples: https://opendev.org/openstack/heat-templates
171+ * Agents: https://opendev.org/openstack/heat-agents
172+ * Release Notes: https://docs.openstack.org/releasenotes/heat/
173+
174+ Python client
175+ -------------
176+
177+ * Documentation: https://docs.openstack.org/python-heatclient/latest
178+ * Source: https://opendev.org/openstack/python-heatclient
179+
180+ Report a Story (a bug/blueprint)
181+ --------------------------------
182+
183+ If you'd like to report a Story (we used to call a bug/blueprint), you can
184+ report it under Report a story in
185+ `Heat's StoryBoard <https://storyboard.openstack.org/#!/project/989>`_.
186+ If you must report the story under other sub-project of heat, you can find
187+ them all in `Heat StoryBoard Group <https://storyboard.openstack.org/#!/project_group/82>`_.
188+ if you encounter any issue.
189+
190+ References
191+ ----------
192+ * https://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_CreateStack.html
193+ * https://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/create-stack.html
194+ * https://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html
195+ * https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=tosca
196+
197+ We have integration with
198+ ------------------------
199+ * https://opendev.org/openstack/python-novaclient (instance)
200+ * https://opendev.org/openstack/python-keystoneclient (auth)
201+ * https://opendev.org/openstack/python-swiftclient (object storage)
202+ * https://opendev.org/openstack/python-neutronclient (networking)
203+ * https://opendev.org/openstack/python-aodhclient (alarming service)
204+ * https://opendev.org/openstack/python-cinderclient (block storage)
205+ * https://opendev.org/openstack/python-glanceclient (image service)
206+ * https://opendev.org/openstack/python-troveclient (database as a Service)
207+ * https://opendev.org/openstack/python-saharaclient (hadoop cluster)
208+ * https://opendev.org/openstack/python-barbicanclient (key management service)
209+ * https://opendev.org/openstack/python-designateclient (DNS service)
210+ * https://opendev.org/openstack/python-magnumclient (container service)
211+ * https://opendev.org/openstack/python-manilaclient (shared file system service)
212+ * https://opendev.org/openstack/python-mistralclient (workflow service)
213+ * https://opendev.org/openstack/python-zaqarclient (messaging service)
214+ * https://opendev.org/openstack/python-monascaclient (monitoring service)
215+ * https://opendev.org/openstack/python-zunclient (container management service)
216+ * https://opendev.org/openstack/python-blazarclient (reservation service)
217+ * https://opendev.org/openstack/python-octaviaclient.git (Load-balancer service)
218+ * https://opendev.org/openstack/python-senlinclient (Clustering service)
219+ * https://opendev.org/openstack/python-vitrageclient.git (RCA service)
220+ * https://opendev.org/openstack/python-ironicclient (baremetal provisioning service)
221+
222+
223+Platform: UNKNOWN
224 Classifier: Environment :: OpenStack
225 Classifier: Intended Audience :: Information Technology
226 Classifier: Intended Audience :: System Administrators
227@@ -18,85 +101,3 @@ Classifier: Programming Language :: Python :: 3.9
228 Classifier: Programming Language :: Python :: 3.10
229 Classifier: Programming Language :: Python :: 3.11
230 Requires-Python: >=3.8
231-License-File: LICENSE
232-
233-========================
234-Team and repository tags
235-========================
236-
237-.. image:: https://governance.openstack.org/tc/badges/heat.svg
238- :target: https://governance.openstack.org/tc/reference/tags/index.html
239-
240-.. Change things from this point on
241-
242-====
243-Heat
244-====
245-
246-Heat is a service to orchestrate multiple composite cloud applications using
247-templates, through both an OpenStack-native REST API and a
248-CloudFormation-compatible Query API.
249-
250-Why heat? It makes the clouds rise and keeps them there.
251-
252-Getting Started
253----------------
254-
255-If you'd like to run from the master branch, you can clone the git repo:
256-
257- git clone https://opendev.org/openstack/heat
258-
259-
260-* Documentation: https://docs.openstack.org/heat/latest
261-* Template samples: https://opendev.org/openstack/heat-templates
262-* Agents: https://opendev.org/openstack/heat-agents
263-* Release Notes: https://docs.openstack.org/releasenotes/heat/
264-
265-Python client
266--------------
267-
268-* Documentation: https://docs.openstack.org/python-heatclient/latest
269-* Source: https://opendev.org/openstack/python-heatclient
270-
271-Report a Story (a bug/blueprint)
272---------------------------------
273-
274-If you'd like to report a Story (we used to call a bug/blueprint), you can
275-report it under Report a story in
276-`Heat's StoryBoard <https://storyboard.openstack.org/#!/project/989>`_.
277-If you must report the story under other sub-project of heat, you can find
278-them all in `Heat StoryBoard Group <https://storyboard.openstack.org/#!/project_group/82>`_.
279-if you encounter any issue.
280-
281-References
282-----------
283-* https://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_CreateStack.html
284-* https://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/create-stack.html
285-* https://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html
286-* https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=tosca
287-
288-We have integration with
289-------------------------
290-* https://opendev.org/openstack/python-novaclient (instance)
291-* https://opendev.org/openstack/python-keystoneclient (auth)
292-* https://opendev.org/openstack/python-swiftclient (object storage)
293-* https://opendev.org/openstack/python-neutronclient (networking)
294-* https://opendev.org/openstack/python-aodhclient (alarming service)
295-* https://opendev.org/openstack/python-cinderclient (block storage)
296-* https://opendev.org/openstack/python-glanceclient (image service)
297-* https://opendev.org/openstack/python-troveclient (database as a Service)
298-* https://opendev.org/openstack/python-saharaclient (hadoop cluster)
299-* https://opendev.org/openstack/python-barbicanclient (key management service)
300-* https://opendev.org/openstack/python-designateclient (DNS service)
301-* https://opendev.org/openstack/python-magnumclient (container service)
302-* https://opendev.org/openstack/python-manilaclient (shared file system service)
303-* https://opendev.org/openstack/python-mistralclient (workflow service)
304-* https://opendev.org/openstack/python-zaqarclient (messaging service)
305-* https://opendev.org/openstack/python-monascaclient (monitoring service)
306-* https://opendev.org/openstack/python-zunclient (container management service)
307-* https://opendev.org/openstack/python-blazarclient (reservation service)
308-* https://opendev.org/openstack/python-octaviaclient.git (Load-balancer service)
309-* https://opendev.org/openstack/python-senlinclient (Clustering service)
310-* https://opendev.org/openstack/python-vitrageclient.git (RCA service)
311-* https://opendev.org/openstack/python-ironicclient (baremetal provisioning service)
312-
313diff --git a/config-generator.conf b/config-generator.conf
314index deb30da..115880d 100644
315--- a/config-generator.conf
316+++ b/config-generator.conf
317@@ -1,16 +1,19 @@
318 [DEFAULT]
319 output_file = etc/heat/heat.conf.sample
320 wrap_width = 79
321+namespace = heat.common.cache
322 namespace = heat.common.config
323 namespace = heat.common.context
324 namespace = heat.common.crypt
325 namespace = heat.engine.clients.os.keystone.heat_keystoneclient
326+namespace = heat.engine.hot.functions
327 namespace = heat.common.wsgi
328 namespace = heat.engine.clients
329 namespace = heat.engine.notification
330 namespace = heat.engine.resources
331 namespace = heat.api.aws.ec2token
332 namespace = keystonemiddleware.auth_token
333+namespace = oslo.cache
334 namespace = oslo.messaging
335 namespace = oslo.middleware
336 namespace = oslo.cache
337diff --git a/debian/changelog b/debian/changelog
338index 53d777f..c31a8e4 100644
339--- a/debian/changelog
340+++ b/debian/changelog
341@@ -1,8 +1,14 @@
342-heat (1:21.0.0+git2024011916.2df46d4c-0ubuntu2) UNRELEASED; urgency=medium
343+heat (1:22.0.0~rc1-0ubuntu1) UNRELEASED; urgency=medium
344
345+ [ James Page ]
346 * d/watch: Track Caracal series releases.
347
348- -- James Page <james.page@ubuntu.com> Tue, 19 Mar 2024 15:37:42 +0000
349+ [ Alex Kavanagh (Canonical Work Key) ]
350+ * New upstream release canadidate (rc1) for OpenStack Caracal
351+ * d/p/*: Refresh.
352+ * d/p/python3.13-compat dropped as patch added upstream.
353+
354+ -- Alex Kavanagh (Canonical Work Key) <alex.kavanagh@canonical.com> Mon, 25 Mar 2024 11:56:44 +0000
355
356 heat (1:21.0.0+git2024011916.2df46d4c-0ubuntu1) noble; urgency=medium
357
358diff --git a/debian/patches/drop-zun.patch b/debian/patches/drop-zun.patch
359index 2e4d9f9..988ba4d 100644
360--- a/debian/patches/drop-zun.patch
361+++ b/debian/patches/drop-zun.patch
362@@ -8,12 +8,12 @@ Forwarded: no
363
364 --- a/requirements.txt
365 +++ b/requirements.txt
366-@@ -50,7 +50,6 @@
367+@@ -50,7 +50,6 @@ python-swiftclient>=3.2.0 # Apache-2.0
368 python-troveclient>=2.2.0 # Apache-2.0
369 python-vitrageclient>=2.7.0 # Apache-2.0
370 python-zaqarclient>=1.3.0 # Apache-2.0
371 -python-zunclient>=3.4.0 # Apache-2.0
372- pytz>=2013.6 # MIT
373+ pytz>=2013.6;python_version<"3.9" # MIT
374 PyYAML>=5.1 # MIT
375 requests>=2.23.0 # Apache-2.0
376 --- a/heat/tests/openstack/zun/test_container.py
377@@ -1250,7 +1250,7 @@ Forwarded: no
378 - }
379 --- a/setup.cfg
380 +++ b/setup.cfg
381-@@ -87,7 +87,6 @@
382+@@ -89,7 +89,6 @@ heat.clients =
383 trove = heat.engine.clients.os.trove:TroveClientPlugin
384 vitrage = heat.engine.clients.os.vitrage:VitrageClientPlugin
385 zaqar = heat.engine.clients.os.zaqar:ZaqarClientPlugin
386diff --git a/debian/patches/python3.12-compat.patch b/debian/patches/python3.12-compat.patch
387deleted file mode 100644
388index 2b056da..0000000
389--- a/debian/patches/python3.12-compat.patch
390+++ /dev/null
391@@ -1,63 +0,0 @@
392-Description: Use assert_has_calls in unit tests
393- Test when executed under Python 3.12 FAIL due to:
394- AttributeError: 'has_calls' is not a valid assertion
395- Use correct assert_has_calls instead
396-
397- This also revealed a problem with the expected parameters
398- on one of the calls in the test_software_config module.
399-Author: James Page <james.page@ubuntu.com>
400-Forwarded: no
401-
402---- a/heat/tests/engine/service/test_software_config.py
403-+++ b/heat/tests/engine/service/test_software_config.py
404-@@ -753,12 +753,12 @@
405- }
406- self.engine.software_config._push_metadata_software_deployments(
407- self.ctx, '1234', None)
408-- res_upd.has_calls(
409-+ res_upd.assert_has_calls([
410- mock.call(self.ctx, '1234',
411- {'rsrc_metadata': result_metadata}, 1),
412- mock.call(self.ctx, '1234',
413-- {'rsrc_metadata': result_metadata}, 2),
414-- )
415-+ {}, 2),
416-+ ])
417-
418- put.assert_called_once_with(
419- 'http://192.168.2.2/foo/bar', json.dumps(result_metadata))
420---- a/heat/tests/test_resource.py
421-+++ b/heat/tests/test_resource.py
422-@@ -4092,8 +4092,8 @@
423- service_type='test_type',
424- service_name=(generic_rsrc.ResourceWithDefaultClientName
425- .default_client_name))
426-- mock_client_plugin.has_extension.has_calls(
427-- [('foo'), ('bar')])
428-+ mock_client_plugin.has_extension.assert_has_calls(
429-+ [mock.call('foo'), mock.call('bar')])
430-
431- @mock.patch.object(clients.OpenStackClients, 'client_plugin')
432- def test_service_deployed_required_extension_true_list(
433-@@ -4118,8 +4118,8 @@
434- service_type='test_type',
435- service_name=(generic_rsrc.ResourceWithDefaultClientName
436- .default_client_name))
437-- mock_client_plugin.has_extension.has_calls(
438-- [('foo'), ('bar')])
439-+ mock_client_plugin.has_extension.assert_has_calls(
440-+ [mock.call('foo'), mock.call('bar')])
441-
442- @mock.patch.object(clients.OpenStackClients, 'client_plugin')
443- def test_service_deployed_required_extension_true_list_fail(
444-@@ -4144,8 +4144,8 @@
445- service_type='test_type',
446- service_name=(generic_rsrc.ResourceWithDefaultClientName
447- .default_client_name))
448-- mock_client_plugin.has_extension.has_calls(
449-- [('foo'), ('bar')])
450-+ mock_client_plugin.has_extension.assert_has_calls(
451-+ [mock.call('foo'), mock.call('bar')])
452-
453-
454- class TestLiveStateUpdate(common.HeatTestCase):
455diff --git a/debian/patches/series b/debian/patches/series
456index 33e20eb..e394474 100644
457--- a/debian/patches/series
458+++ b/debian/patches/series
459@@ -1,4 +1,3 @@
460 drop-zun.patch
461 sudoers_patch.patch
462 install-missing-files.patch
463-python3.12-compat.patch
464diff --git a/debian/patches/sudoers_patch.patch b/debian/patches/sudoers_patch.patch
465index 02b91b4..4fc05da 100644
466--- a/debian/patches/sudoers_patch.patch
467+++ b/debian/patches/sudoers_patch.patch
468@@ -3,7 +3,7 @@ Author: Chuck Short <zulcss@ubuntu.com>
469 Forwarded: Not needed.
470 --- a/heat/engine/clients/os/nova.py
471 +++ b/heat/engine/clients/os/nova.py
472-@@ -360,8 +360,8 @@
473+@@ -360,8 +360,8 @@ class NovaClientPlugin(microversion_mixi
474 #
475 # See bug https://bugs.launchpad.net/heat/+bug/1257410
476 boothook_custom_user = r"""useradd -m %s
477diff --git a/devstack/lib/heat b/devstack/lib/heat
478index 6d02e7d..198b3d3 100644
479--- a/devstack/lib/heat
480+++ b/devstack/lib/heat
481@@ -449,20 +449,9 @@ function configure_tempest_for_heat {
482 SKIP_SCENARIO_TEST_LIST+=',AodhAlarmTest'
483 # Skip CfnInitIntegrationTest as latest fedora images don't have heat-cfntools
484 SKIP_SCENARIO_TEST_LIST+=',CfnInitIntegrationTest'
485- if is_ubuntu; then
486- # Skip a few tests failing because of a known libvirt issue in Jammy
487- # https://bugs.launchpad.net/nova/+bug/1998274
488- SKIP_SCENARIO_TEST_LIST+=',BasicResourcesTest'
489- fi
490 iniset $TEMPEST_CONFIG heat_plugin skip_scenario_test_list $SKIP_SCENARIO_TEST_LIST
491
492 SKIP_FUNCTIONAL_TEST_LIST='NotificationTest'
493- if is_ubuntu; then
494- # Skip a few tests failing because of a known libvirt issue in Jammy
495- # https://bugs.launchpad.net/nova/+bug/1998274
496- SKIP_FUNCTIONAL_TEST_LIST+=',UpdateStackTest.test_stack_update_with_replacing_userdata'
497- SKIP_FUNCTIONAL_TEST_LIST+=',CancelUpdateTest.test_cancel_update_server_with_port'
498- fi
499 iniset $TEMPEST_CONFIG heat_plugin skip_functional_test_list $SKIP_FUNCTIONAL_TEST_LIST
500
501 openstack flavor show m1.heat_int || openstack flavor create m1.heat_int --ram 1024 --vcpus 2 --disk 10
502diff --git a/doc/source/conf.py b/doc/source/conf.py
503index 5c41d69..6d4c2d6 100644
504--- a/doc/source/conf.py
505+++ b/doc/source/conf.py
506@@ -33,8 +33,9 @@ from oslo_config import cfg
507
508 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
509 ROOT = os.path.abspath(os.path.join(BASE_DIR, "..", ".."))
510-CONTRIB_DIR = os.path.join(ROOT, 'contrib')
511-PLUGIN_DIRS = glob.glob(os.path.join(CONTRIB_DIR, '*'))
512+# TODO(tkajinam): Fix this
513+# CONTRIB_DIR = os.path.join(ROOT, 'contrib')
514+# PLUGIN_DIRS = glob.glob(os.path.join(CONTRIB_DIR, '*'))
515 ENV_DIR = os.path.join(ROOT, "etc", "heat", "environment.d")
516 TEMP_ENV_DIR = tempfile.mkdtemp()
517
518@@ -48,7 +49,7 @@ sys.path.insert(0, ROOT)
519 sys.path.insert(0, BASE_DIR)
520
521 cfg.CONF.import_opt('plugin_dirs', 'heat.common.config')
522-cfg.CONF.set_override(name='plugin_dirs', override=PLUGIN_DIRS)
523+# cfg.CONF.set_override(name='plugin_dirs', override=PLUGIN_DIRS)
524
525 cfg.CONF.import_opt('environment_dir', 'heat.common.config')
526 cfg.CONF.set_override(name='environment_dir', override=TEMP_ENV_DIR)
527diff --git a/doc/source/operating_guides/upgrades_guide.rst b/doc/source/operating_guides/upgrades_guide.rst
528index e1dfa08..6144184 100644
529--- a/doc/source/operating_guides/upgrades_guide.rst
530+++ b/doc/source/operating_guides/upgrades_guide.rst
531@@ -40,7 +40,7 @@ Cold Upgrades
532 Heat already supports "`cold-upgrades`_", where the heat services have to be
533 down during the upgrade. For time-consuming upgrades, it may be unacceptable
534 for the services to be unavailable for a long period of time. This type of
535-upgrade is quite simple, follow the bellow steps:
536+upgrade is quite simple, follow the below steps:
537
538 1. Stop all heat-api and heat-engine services.
539
540diff --git a/doc/source/template_guide/multi-clouds.rst b/doc/source/template_guide/multi-clouds.rst
541index e6266db..0038725 100644
542--- a/doc/source/template_guide/multi-clouds.rst
543+++ b/doc/source/template_guide/multi-clouds.rst
544@@ -124,7 +124,7 @@ To create a remote stack, you can simply use an :ref:`OS::Heat::Stack` resource
545 in your template.
546
547 In resource properties, provide `credential_secret_id` (Barbican secret ID
548-from the secret we just builded for credential) under `context` property.
549+from the secret we just built for credential) under `context` property.
550
551 Here is an template example for you:
552
553diff --git a/heat/api/openstack/v1/util.py b/heat/api/openstack/v1/util.py
554index ee2b92d..556f6f2 100644
555--- a/heat/api/openstack/v1/util.py
556+++ b/heat/api/openstack/v1/util.py
557@@ -29,9 +29,15 @@ def registered_policy_enforce(handler):
558 """
559 @functools.wraps(handler)
560 def handle_stack_method(controller, req, tenant_id, **kwargs):
561+ # NOTE(tkajinam): Heat uses stack owner's project id in redirect URI.
562+ # So admin might be redirected to different project id when accessing
563+ # resources in a different project. Use project id in context to
564+ # bypass project_id check, because admin should have access to all
565+ # projects.
566+ if req.context.is_admin and req.context.project_id:
567+ tenant_id = req.context.tenant_id
568 _target = {"project_id": tenant_id}
569-
570- if req.context.tenant_id != tenant_id and not req.context.is_admin:
571+ if req.context.tenant_id != tenant_id:
572 raise exc.HTTPForbidden()
573 allowed = req.context.policy.enforce(
574 context=req.context,
575diff --git a/heat/cloudinit/loguserdata.py b/heat/cloudinit/loguserdata.py
576index 14d17f2..810e353 100755
577--- a/heat/cloudinit/loguserdata.py
578+++ b/heat/cloudinit/loguserdata.py
579@@ -11,61 +11,19 @@
580 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
581 # License for the specific language governing permissions and limitations
582 # under the License.
583-"true" '''\'
584-# NOTE(vgridnev): ubuntu trusty by default has python3,
585-# but pkg_resources can't be imported.
586-echo "import pkg_resources" | python3 2>/dev/null
587-has_py3=$?
588-echo "import pkg_resources" | python2 2>/dev/null
589-has_py2=$?
590-echo "import pkg_resources" | /usr/libexec/platform-python 2>/dev/null
591-has_platform-py=$?
592-
593-if [ $has_py3 = 0 ]; then
594- interpreter="python3"
595-elif [ $has_py2 = 0 ]; then
596- interpreter="python"
597-elif [ $has_platform-py = 0 ]; then
598- interpreter="/usr/libexec/platform-python"
599-else
600- interpreter="python"
601-fi
602-exec $interpreter "$0"
603-'''
604
605 import datetime
606 import errno
607 import logging
608 import os
609-import re
610 import subprocess
611 import sys
612
613-from packaging import version
614-import pkg_resources
615-
616
617 VAR_PATH = '/var/lib/heat-cfntools'
618 LOG = logging.getLogger('heat-provision')
619
620
621-def chk_ci_version():
622- try:
623- v = version.Version(
624- pkg_resources.get_distribution('cloud-init').version)
625- return v >= version.Version('0.6.0')
626- except Exception:
627- pass
628- data = subprocess.Popen(['cloud-init', '--version'],
629- stdout=subprocess.PIPE,
630- stderr=subprocess.PIPE).communicate()
631- if data[0]:
632- raise Exception()
633- # data[1] has such format: 'cloud-init 0.7.5\n', need to parse version
634- v = re.split(' |\n', data[1])[1].split('.')
635- return tuple(v) >= tuple(['0', '6', '0'])
636-
637-
638 def init_logging():
639 LOG.setLevel(logging.INFO)
640 LOG.addHandler(logging.StreamHandler())
641@@ -106,17 +64,6 @@ def call(args):
642
643
644 def main():
645-
646- try:
647- if not chk_ci_version():
648- # pre 0.6.0 - user data executed via cloudinit, not this helper
649- LOG.error('Unable to log provisioning, need a newer version of '
650- 'cloud-init')
651- return -1
652- except Exception:
653- LOG.warning('Can not determine the version of cloud-init. It is '
654- 'possible to get errors while logging provisioning.')
655-
656 userdata_path = os.path.join(VAR_PATH, 'cfn-userdata')
657 os.chmod(userdata_path, int("700", 8))
658
659diff --git a/heat/cloudinit/part_handler.py b/heat/cloudinit/part_handler.py
660index 50318f3..cf879fe 100644
661--- a/heat/cloudinit/part_handler.py
662+++ b/heat/cloudinit/part_handler.py
663@@ -19,7 +19,7 @@ import sys
664
665
666 def list_types():
667- return(["text/x-cfninitdata"])
668+ return ["text/x-cfninitdata"]
669
670
671 def handle_part(data, ctype, filename, payload):
672diff --git a/heat/common/cache.py b/heat/common/cache.py
673index 3e011fd..6dc1d95 100644
674--- a/heat/common/cache.py
675+++ b/heat/common/cache.py
676@@ -21,6 +21,56 @@ from oslo_config import cfg
677
678 from heat.common.i18n import _
679
680+constraint_cache_group = cfg.OptGroup('constraint_validation_cache')
681+constraint_cache_opts = [
682+ cfg.IntOpt('expiration_time', default=60,
683+ help=_(
684+ 'TTL, in seconds, for any cached item in the '
685+ 'dogpile.cache region used for caching of validation '
686+ 'constraints.')),
687+ cfg.BoolOpt("caching", default=True,
688+ help=_(
689+ 'Toggle to enable/disable caching when Orchestration '
690+ 'Engine validates property constraints of stack. '
691+ 'During property validation with constraints '
692+ 'Orchestration Engine caches requests to other '
693+ 'OpenStack services. Please note that the global '
694+ 'toggle for oslo.cache(enabled=True in [cache] group) '
695+ 'must be enabled to use this feature.'))
696+]
697+
698+extension_cache_group = cfg.OptGroup('service_extension_cache')
699+extension_cache_opts = [
700+ cfg.IntOpt('expiration_time', default=3600,
701+ help=_(
702+ 'TTL, in seconds, for any cached item in the '
703+ 'dogpile.cache region used for caching of service '
704+ 'extensions.')),
705+ cfg.BoolOpt('caching', default=True,
706+ help=_(
707+ 'Toggle to enable/disable caching when Orchestration '
708+ 'Engine retrieves extensions from other OpenStack '
709+ 'services. Please note that the global toggle for '
710+ 'oslo.cache(enabled=True in [cache] group) must be '
711+ 'enabled to use this feature.'))
712+]
713+
714+find_cache_group = cfg.OptGroup('resource_finder_cache')
715+find_cache_opts = [
716+ cfg.IntOpt('expiration_time', default=3600,
717+ help=_(
718+ 'TTL, in seconds, for any cached item in the '
719+ 'dogpile.cache region used for caching of OpenStack '
720+ 'service finder functions.')),
721+ cfg.BoolOpt('caching', default=True,
722+ help=_(
723+ 'Toggle to enable/disable caching when Orchestration '
724+ 'Engine looks for other OpenStack service resources '
725+ 'using name or id. Please note that the global '
726+ 'toggle for oslo.cache(enabled=True in [cache] group) '
727+ 'must be enabled to use this feature.'))
728+]
729+
730
731 def register_cache_configurations(conf):
732 """Register all configurations required for oslo.cache.
733@@ -35,65 +85,24 @@ def register_cache_configurations(conf):
734 core.configure(conf)
735
736 # register heat specific configurations
737- constraint_cache_group = cfg.OptGroup('constraint_validation_cache')
738- constraint_cache_opts = [
739- cfg.IntOpt('expiration_time', default=60,
740- help=_(
741- 'TTL, in seconds, for any cached item in the '
742- 'dogpile.cache region used for caching of validation '
743- 'constraints.')),
744- cfg.BoolOpt("caching", default=True,
745- help=_(
746- 'Toggle to enable/disable caching when Orchestration '
747- 'Engine validates property constraints of stack. '
748- 'During property validation with constraints '
749- 'Orchestration Engine caches requests to other '
750- 'OpenStack services. Please note that the global '
751- 'toggle for oslo.cache(enabled=True in [cache] group) '
752- 'must be enabled to use this feature.'))
753- ]
754 conf.register_group(constraint_cache_group)
755 conf.register_opts(constraint_cache_opts, group=constraint_cache_group)
756
757- extension_cache_group = cfg.OptGroup('service_extension_cache')
758- extension_cache_opts = [
759- cfg.IntOpt('expiration_time', default=3600,
760- help=_(
761- 'TTL, in seconds, for any cached item in the '
762- 'dogpile.cache region used for caching of service '
763- 'extensions.')),
764- cfg.BoolOpt('caching', default=True,
765- help=_(
766- 'Toggle to enable/disable caching when Orchestration '
767- 'Engine retrieves extensions from other OpenStack '
768- 'services. Please note that the global toggle for '
769- 'oslo.cache(enabled=True in [cache] group) must be '
770- 'enabled to use this feature.'))
771- ]
772 conf.register_group(extension_cache_group)
773 conf.register_opts(extension_cache_opts, group=extension_cache_group)
774
775- find_cache_group = cfg.OptGroup('resource_finder_cache')
776- find_cache_opts = [
777- cfg.IntOpt('expiration_time', default=3600,
778- help=_(
779- 'TTL, in seconds, for any cached item in the '
780- 'dogpile.cache region used for caching of OpenStack '
781- 'service finder functions.')),
782- cfg.BoolOpt('caching', default=True,
783- help=_(
784- 'Toggle to enable/disable caching when Orchestration '
785- 'Engine looks for other OpenStack service resources '
786- 'using name or id. Please note that the global '
787- 'toggle for oslo.cache(enabled=True in [cache] group) '
788- 'must be enabled to use this feature.'))
789- ]
790 conf.register_group(find_cache_group)
791 conf.register_opts(find_cache_opts, group=find_cache_group)
792
793 return conf
794
795
796+def list_opts():
797+ yield constraint_cache_group.name, constraint_cache_opts
798+ yield extension_cache_group.name, extension_cache_opts
799+ yield find_cache_group.name, find_cache_opts
800+
801+
802 # variable that stores an initialized cache region for heat
803 _REGION = None
804
805diff --git a/heat/common/config.py b/heat/common/config.py
806index 1e1df6f..c6a1dfc 100644
807--- a/heat/common/config.py
808+++ b/heat/common/config.py
809@@ -152,6 +152,19 @@ engine_opts = [
810 default=512,
811 help=_('Maximum number of stacks any one tenant may have '
812 'active at one time. -1 stands for unlimited.')),
813+ cfg.IntOpt('max_software_configs_per_tenant',
814+ default=4096,
815+ help=_('Maximum number of software configs any one tenant may '
816+ 'have active at one time. -1 stands for unlimited.')),
817+ cfg.IntOpt('max_software_deployments_per_tenant',
818+ default=4096,
819+ help=_('Maximum number of software deployments any one tenant '
820+ 'may have active at one time.'
821+ '-1 stands for unlimited.')),
822+ cfg.IntOpt('max_snapshots_per_stack',
823+ default=32,
824+ help=_('Maximum number of snapshot any one stack may have '
825+ 'active at one time. -1 stands for unlimited.')),
826 cfg.IntOpt('action_retry_limit',
827 default=5,
828 help=_('Number of times to retry to bring a '
829diff --git a/heat/common/plugin_loader.py b/heat/common/plugin_loader.py
830index 9023374..7ea933d 100644
831--- a/heat/common/plugin_loader.py
832+++ b/heat/common/plugin_loader.py
833@@ -20,6 +20,7 @@ for them before loading them.
834 """
835
836 import functools
837+import importlib.util
838 import pkgutil
839 import sys
840 import types
841@@ -70,7 +71,8 @@ def _import_module(importer, module_name, package):
842 if module_spec is None:
843 return None
844
845- module = module_spec.loader.load_module(module_name)
846+ module = importlib.util.module_from_spec(module_spec)
847+ module_spec.loader.exec_module(module)
848
849 # Make this accessible through the parent package for static imports
850 local_name = module_name.partition(package.__name__ + '.')[2]
851diff --git a/heat/db/api.py b/heat/db/api.py
852index 70aab59..0d03547 100644
853--- a/heat/db/api.py
854+++ b/heat/db/api.py
855@@ -1431,6 +1431,14 @@ def software_config_get_all(context, limit=None, marker=None):
856 limit=limit, marker=marker).all()
857
858
859+@context_manager.reader
860+def software_config_count_all(context):
861+ query = context.session.query(models.SoftwareConfig)
862+ if not context.is_admin:
863+ query = query.filter_by(tenant=context.tenant_id)
864+ return query.count()
865+
866+
867 @context_manager.writer
868 def software_config_delete(context, config_id):
869 config = _software_config_get(context, config_id)
870@@ -1510,6 +1518,21 @@ def software_deployment_get_all(context, server_id=None):
871 return query.all()
872
873
874+@context_manager.reader
875+def software_deployment_count_all(context):
876+ sd = models.SoftwareDeployment
877+ query = context.session.query(sd)
878+ if not context.is_admin:
879+ query = query.filter(
880+ sqlalchemy.or_(
881+ sd.tenant == context.tenant_id,
882+ sd.stack_user_project_id == context.tenant_id,
883+ )
884+ )
885+
886+ return query.count()
887+
888+
889 @context_manager.writer
890 def software_deployment_update(context, deployment_id, values):
891 deployment = _software_deployment_get(context, deployment_id)
892@@ -1582,11 +1605,17 @@ def snapshot_delete(context, snapshot_id):
893
894
895 @context_manager.reader
896-def snapshot_get_all(context, stack_id):
897+def snapshot_get_all_by_stack(context, stack_id):
898 return context.session.query(models.Snapshot).filter_by(
899 stack_id=stack_id, tenant=context.tenant_id)
900
901
902+@context_manager.reader
903+def snapshot_count_all_by_stack(context, stack_id):
904+ return context.session.query(models.Snapshot).filter_by(
905+ stack_id=stack_id, tenant=context.tenant_id).count()
906+
907+
908 # service
909
910
911diff --git a/heat/engine/clients/os/keystone/fake_keystoneclient.py b/heat/engine/clients/os/keystone/fake_keystoneclient.py
912index 9715c95..d2d2879 100644
913--- a/heat/engine/clients/os/keystone/fake_keystoneclient.py
914+++ b/heat/engine/clients/os/keystone/fake_keystoneclient.py
915@@ -51,7 +51,7 @@ class FakeKeystoneClient(object):
916 secret = self.secret
917 self.creds = FakeCred()
918
919- def create_stack_user(self, username, password=''):
920+ def create_stack_user(self, username, password):
921 self.username = username
922 return self.user_id
923
924diff --git a/heat/engine/clients/os/keystone/heat_keystoneclient.py b/heat/engine/clients/os/keystone/heat_keystoneclient.py
925index 7f993ec..2cf8ee9 100644
926--- a/heat/engine/clients/os/keystone/heat_keystoneclient.py
927+++ b/heat/engine/clients/os/keystone/heat_keystoneclient.py
928@@ -277,13 +277,13 @@ class KsClientWrapper(object):
929 return trust_context
930
931 def _get_username(self, username):
932- if(len(username) > 255):
933+ if len(username) > 255:
934 LOG.warning("Truncating the username %s to the last 255 "
935 "characters.", username)
936 # get the last 255 characters of the username
937 return username[-255:]
938
939- def create_stack_user(self, username, password=''):
940+ def create_stack_user(self, username, password):
941 """Create a user defined as part of a stack.
942
943 The user is defined either via template or created internally by a
944diff --git a/heat/engine/constraint/common_constraints.py b/heat/engine/constraint/common_constraints.py
945index cef2571..5588ef7 100644
946--- a/heat/engine/constraint/common_constraints.py
947+++ b/heat/engine/constraint/common_constraints.py
948@@ -14,7 +14,13 @@
949 import croniter
950 import eventlet
951 import netaddr
952-import pytz
953+
954+try:
955+ import zoneinfo
956+except ImportError:
957+ # zoneinfo is available in Python >= 3.9
958+ import pytz
959+ zoneinfo = None
960
961 from neutron_lib.api import validators
962 from oslo_utils import timeutils
963@@ -106,7 +112,7 @@ class CIDRConstraint(constraints.BaseCustomConstraint):
964
965 def validate(self, value, context, template=None):
966 try:
967- netaddr.IPNetwork(value, implicit_prefix=True)
968+ netaddr.IPNetwork(netaddr.cidr_abbrev_to_verbose(value))
969 msg = validators.validate_subnet(value)
970 if msg is not None:
971 self._error_message = msg
972@@ -167,7 +173,10 @@ class TimezoneConstraint(constraints.BaseCustomConstraint):
973 if not value:
974 return True
975 try:
976- pytz.timezone(value)
977+ if zoneinfo:
978+ zoneinfo.ZoneInfo(value)
979+ else:
980+ pytz.timezone(value)
981 return True
982 except Exception as ex:
983 self._error_message = _(
984diff --git a/heat/engine/hot/functions.py b/heat/engine/hot/functions.py
985index ec4bb9d..0e63574 100644
986--- a/heat/engine/hot/functions.py
987+++ b/heat/engine/hot/functions.py
988@@ -31,7 +31,8 @@ from heat.engine import function
989
990 LOG = logging.getLogger(__name__)
991
992-opts = [
993+yaql_group = cfg.OptGroup('yaql')
994+yaql_opts = [
995 cfg.IntOpt('limit_iterators',
996 default=200,
997 help=_('The maximum number of elements in collection '
998@@ -41,7 +42,11 @@ opts = [
999 help=_('The maximum size of memory in bytes that '
1000 'expression can take for its evaluation.'))
1001 ]
1002-cfg.CONF.register_opts(opts, group='yaql')
1003+cfg.CONF.register_opts(yaql_opts, group=yaql_group)
1004+
1005+
1006+def list_opts():
1007+ yield yaql_group.name, yaql_opts
1008
1009
1010 class GetParam(function.Function):
1011diff --git a/heat/engine/resource.py b/heat/engine/resource.py
1012index c8e5c0e..e40fdb9 100644
1013--- a/heat/engine/resource.py
1014+++ b/heat/engine/resource.py
1015@@ -809,7 +809,7 @@ class Resource(status.ResourceStatus):
1016 if endpoint_exists:
1017 req_extension = cls.required_service_extension
1018 if not req_extension:
1019- return(True, None)
1020+ return (True, None)
1021 if isinstance(req_extension, str):
1022 req_extension = re.split(' |,', req_extension)
1023 for ext in req_extension:
1024diff --git a/heat/engine/resources/openstack/monasca/alarm_definition.py b/heat/engine/resources/openstack/monasca/alarm_definition.py
1025index 3af1fa2..dea669e 100644
1026--- a/heat/engine/resources/openstack/monasca/alarm_definition.py
1027+++ b/heat/engine/resources/openstack/monasca/alarm_definition.py
1028@@ -32,11 +32,15 @@ class MonascaAlarmDefinition(resource.Resource):
1029 """
1030
1031 support_status = support.SupportStatus(
1032- version='7.0.0',
1033+ version='22.0.0',
1034+ status=support.DEPRECATED,
1035+ message=_('Monasca project was marked inactive'),
1036 previous_status=support.SupportStatus(
1037- version='5.0.0',
1038- status=support.UNSUPPORTED
1039- ))
1040+ version='7.0.0',
1041+ previous_status=support.SupportStatus(
1042+ version='5.0.0',
1043+ status=support.UNSUPPORTED
1044+ )))
1045
1046 default_client_name = 'monasca'
1047
1048diff --git a/heat/engine/resources/openstack/monasca/notification.py b/heat/engine/resources/openstack/monasca/notification.py
1049index 8074bd0..65a155d 100644
1050--- a/heat/engine/resources/openstack/monasca/notification.py
1051+++ b/heat/engine/resources/openstack/monasca/notification.py
1052@@ -32,11 +32,16 @@ class MonascaNotification(resource.Resource):
1053 """
1054
1055 support_status = support.SupportStatus(
1056- version='7.0.0',
1057+ version='22.0.0',
1058+ status=support.DEPRECATED,
1059+ message=_('Monasca project was marked inactive'),
1060 previous_status=support.SupportStatus(
1061- version='5.0.0',
1062- status=support.UNSUPPORTED
1063- ))
1064+ version='7.0.0',
1065+ status=support.SUPPORTED,
1066+ previous_status=support.SupportStatus(
1067+ version='5.0.0',
1068+ status=support.SUPPORTED
1069+ )))
1070
1071 default_client_name = 'monasca'
1072
1073diff --git a/heat/engine/resources/openstack/neutron/port.py b/heat/engine/resources/openstack/neutron/port.py
1074index a06fbf3..58f3029 100644
1075--- a/heat/engine/resources/openstack/neutron/port.py
1076+++ b/heat/engine/resources/openstack/neutron/port.py
1077@@ -545,7 +545,7 @@ class Port(neutron.NeutronResource):
1078 ).get_secgroup_uuids(['default'])
1079
1080 if self.REPLACEMENT_POLICY in props:
1081- del(props[self.REPLACEMENT_POLICY])
1082+ del props[self.REPLACEMENT_POLICY]
1083
1084 def _store_config_default_properties(self, attrs):
1085 """A method for storing properties default values.
1086diff --git a/heat/engine/resources/openstack/neutron/security_group.py b/heat/engine/resources/openstack/neutron/security_group.py
1087index 0bd867c..f88d589 100644
1088--- a/heat/engine/resources/openstack/neutron/security_group.py
1089+++ b/heat/engine/resources/openstack/neutron/security_group.py
1090@@ -180,7 +180,7 @@ class SecurityGroup(neutron.NeutronResource):
1091
1092 if 'remote_mode' in rule:
1093 remote_mode = rule.get(self.RULE_REMOTE_MODE)
1094- del(rule[self.RULE_REMOTE_MODE])
1095+ del rule[self.RULE_REMOTE_MODE]
1096
1097 if remote_mode == self.RULE_REMOTE_GROUP_ID:
1098 rule[self.RULE_REMOTE_IP_PREFIX] = None
1099diff --git a/heat/engine/resources/openstack/sahara/cluster.py b/heat/engine/resources/openstack/sahara/cluster.py
1100index 05a0def..6dc45a6 100644
1101--- a/heat/engine/resources/openstack/sahara/cluster.py
1102+++ b/heat/engine/resources/openstack/sahara/cluster.py
1103@@ -49,6 +49,15 @@ class SaharaCluster(resource.Resource):
1104 should specify a keypair.
1105 """
1106
1107+ support_status = support.SupportStatus(
1108+ version='22.0.0',
1109+ status=support.DEPRECATED,
1110+ message=_('Sahara project was marked inactive'),
1111+ previous_status=support.SupportStatus(
1112+ version='5.0.0',
1113+ status=support.SUPPORTED
1114+ ))
1115+
1116 PROPERTIES = (
1117 NAME, PLUGIN_NAME, HADOOP_VERSION, CLUSTER_TEMPLATE_ID,
1118 KEY_NAME, IMAGE, MANAGEMENT_NETWORK, IMAGE_ID,
1119diff --git a/heat/engine/resources/openstack/sahara/data_source.py b/heat/engine/resources/openstack/sahara/data_source.py
1120index ff41bf4..c793f89 100644
1121--- a/heat/engine/resources/openstack/sahara/data_source.py
1122+++ b/heat/engine/resources/openstack/sahara/data_source.py
1123@@ -26,7 +26,14 @@ class DataSource(resource.Resource):
1124 or output data and any credentials needed to access the location.
1125 """
1126
1127- support_status = support.SupportStatus(version='5.0.0')
1128+ support_status = support.SupportStatus(
1129+ version='22.0.0',
1130+ status=support.DEPRECATED,
1131+ message=_('Sahara project was marked inactive'),
1132+ previous_status=support.SupportStatus(
1133+ version='5.0.0',
1134+ status=support.SUPPORTED
1135+ ))
1136
1137 PROPERTIES = (
1138 NAME, TYPE, URL, DESCRIPTION, CREDENTIALS
1139diff --git a/heat/engine/resources/openstack/sahara/image.py b/heat/engine/resources/openstack/sahara/image.py
1140index dc60c72..152fbb1 100644
1141--- a/heat/engine/resources/openstack/sahara/image.py
1142+++ b/heat/engine/resources/openstack/sahara/image.py
1143@@ -26,7 +26,14 @@ class SaharaImageRegistry(resource.Resource):
1144 Allows to register an image in the sahara image registry and add tags.
1145 """
1146
1147- support_status = support.SupportStatus(version='6.0.0')
1148+ support_status = support.SupportStatus(
1149+ version='22.0.0',
1150+ status=support.DEPRECATED,
1151+ message=_('Sahara project was marked inactive'),
1152+ previous_status=support.SupportStatus(
1153+ version='5.0.0',
1154+ status=support.SUPPORTED
1155+ ))
1156
1157 PROPERTIES = (
1158 IMAGE, USERNAME, DESCRIPTION, TAGS
1159diff --git a/heat/engine/resources/openstack/sahara/job.py b/heat/engine/resources/openstack/sahara/job.py
1160index cda9679..63ca204 100644
1161--- a/heat/engine/resources/openstack/sahara/job.py
1162+++ b/heat/engine/resources/openstack/sahara/job.py
1163@@ -33,7 +33,14 @@ class SaharaJob(signal_responder.SignalResponder, resource.Resource):
1164 job binary objects. Can be launched using resource-signal.
1165 """
1166
1167- support_status = support.SupportStatus(version='8.0.0')
1168+ support_status = support.SupportStatus(
1169+ version='22.0.0',
1170+ status=support.DEPRECATED,
1171+ message=_('Sahara project was marked inactive'),
1172+ previous_status=support.SupportStatus(
1173+ version='8.0.0',
1174+ status=support.SUPPORTED
1175+ ))
1176
1177 PROPERTIES = (
1178 NAME, TYPE, MAINS, LIBS, DESCRIPTION,
1179diff --git a/heat/engine/resources/openstack/sahara/job_binary.py b/heat/engine/resources/openstack/sahara/job_binary.py
1180index 85cf705..f1e05af 100644
1181--- a/heat/engine/resources/openstack/sahara/job_binary.py
1182+++ b/heat/engine/resources/openstack/sahara/job_binary.py
1183@@ -29,7 +29,14 @@ class JobBinary(resource.Resource):
1184 credentials needed to retrieve the file.
1185 """
1186
1187- support_status = support.SupportStatus(version='5.0.0')
1188+ support_status = support.SupportStatus(
1189+ version='22.0.0',
1190+ status=support.DEPRECATED,
1191+ message=_('Sahara project was marked inactive'),
1192+ previous_status=support.SupportStatus(
1193+ version='5.0.0',
1194+ status=support.SUPPORTED
1195+ ))
1196
1197 PROPERTIES = (
1198 NAME, URL, DESCRIPTION, CREDENTIALS
1199diff --git a/heat/engine/resources/openstack/sahara/templates.py b/heat/engine/resources/openstack/sahara/templates.py
1200index 0f52a2a..c49ee01 100644
1201--- a/heat/engine/resources/openstack/sahara/templates.py
1202+++ b/heat/engine/resources/openstack/sahara/templates.py
1203@@ -44,7 +44,14 @@ class SaharaNodeGroupTemplate(resource.Resource):
1204 configurations for those processes.
1205 """
1206
1207- support_status = support.SupportStatus(version='2014.2')
1208+ support_status = support.SupportStatus(
1209+ version='22.0.0',
1210+ status=support.DEPRECATED,
1211+ message=_('Sahara project was marked inactive'),
1212+ previous_status=support.SupportStatus(
1213+ version='2014.2',
1214+ status=support.SUPPORTED
1215+ ))
1216
1217 PROPERTIES = (
1218 NAME, PLUGIN_NAME, HADOOP_VERSION, FLAVOR, DESCRIPTION,
1219diff --git a/heat/engine/resources/openstack/senlin/res_base.py b/heat/engine/resources/openstack/senlin/res_base.py
1220index a294df1..3df3fd6 100644
1221--- a/heat/engine/resources/openstack/senlin/res_base.py
1222+++ b/heat/engine/resources/openstack/senlin/res_base.py
1223@@ -14,6 +14,7 @@
1224
1225 from oslo_log import log as logging
1226
1227+from heat.common.i18n import _
1228 from heat.engine import resource
1229 from heat.engine import support
1230
1231@@ -23,7 +24,13 @@ LOG = logging.getLogger(__name__)
1232 class BaseSenlinResource(resource.Resource):
1233 """A base class for Senlin resources."""
1234
1235- support_status = support.SupportStatus(version='6.0.0')
1236+ support_status = support.SupportStatus(
1237+ version='22.0.0',
1238+ status=support.DEPRECATED,
1239+ message=_('Senlin project was marked inactive'),
1240+ previous_status=support.SupportStatus(
1241+ version='6.0.0',
1242+ ))
1243
1244 default_client_name = 'senlin'
1245
1246diff --git a/heat/engine/service.py b/heat/engine/service.py
1247index 9019ddb..037c912 100644
1248--- a/heat/engine/service.py
1249+++ b/heat/engine/service.py
1250@@ -73,6 +73,10 @@ from heat.rpc import worker_api as rpc_worker_api
1251 cfg.CONF.import_opt('engine_life_check_timeout', 'heat.common.config')
1252 cfg.CONF.import_opt('max_resources_per_stack', 'heat.common.config')
1253 cfg.CONF.import_opt('max_stacks_per_tenant', 'heat.common.config')
1254+cfg.CONF.import_opt('max_snapshots_per_stack', 'heat.common.config')
1255+cfg.CONF.import_opt('max_software_configs_per_tenant', 'heat.common.config')
1256+cfg.CONF.import_opt('max_software_deployments_per_tenant',
1257+ 'heat.common.config')
1258 cfg.CONF.import_opt('enable_stack_abandon', 'heat.common.config')
1259 cfg.CONF.import_opt('enable_stack_adopt', 'heat.common.config')
1260 cfg.CONF.import_opt('convergence_engine', 'heat.common.config')
1261@@ -2124,6 +2128,17 @@ class EngineService(service.ServiceBase):
1262 raise exception.ActionInProgress(stack_name=stack.name,
1263 action=stack.action)
1264
1265+ # Do not enforce the limit, following the stack limit
1266+ if not cnxt.is_admin:
1267+ stack_limit = cfg.CONF.max_snapshots_per_stack
1268+ count_all = snapshot_object.Snapshot.count_all_by_stack(cnxt,
1269+ stack.id)
1270+ if (stack_limit >= 0 and count_all >= stack_limit):
1271+ message = _("You have reached the maximum snapshots "
1272+ "per stack, %d. Please delete some "
1273+ "snapshots.") % stack_limit
1274+ raise exception.RequestLimitExceeded(message=message)
1275+
1276 lock = stack_lock.StackLock(cnxt, stack.id, self.engine_id)
1277
1278 with lock.thread_lock():
1279@@ -2198,7 +2213,7 @@ class EngineService(service.ServiceBase):
1280 @context.request_context
1281 def stack_list_snapshots(self, cnxt, stack_identity):
1282 s = self._get_stack(cnxt, stack_identity)
1283- data = snapshot_object.Snapshot.get_all(cnxt, s.id)
1284+ data = snapshot_object.Snapshot.get_all_by_stack(cnxt, s.id)
1285 return [api.format_snapshot(snapshot) for snapshot in data]
1286
1287 @context.request_context
1288@@ -2219,6 +2234,15 @@ class EngineService(service.ServiceBase):
1289 @context.request_context
1290 def create_software_config(self, cnxt, group, name, config,
1291 inputs, outputs, options):
1292+ # Do not enforce the limit, following the stack limit
1293+ if not cnxt.is_admin:
1294+ tenant_limit = cfg.CONF.max_software_configs_per_tenant
1295+ count_all = self.software_config.count_software_config(cnxt)
1296+ if (tenant_limit >= 0 and count_all >= tenant_limit):
1297+ message = _("You have reached the maximum software configs "
1298+ "per tenant, %d. "
1299+ "Please delete some configs.") % tenant_limit
1300+ raise exception.RequestLimitExceeded(message=message)
1301 return self.software_config.create_software_config(
1302 cnxt,
1303 group=group,
1304@@ -2257,6 +2281,16 @@ class EngineService(service.ServiceBase):
1305 input_values, action, status,
1306 status_reason, stack_user_project_id,
1307 deployment_id=None):
1308+ # Do not enforce the limit, following the stack limit
1309+ if not cnxt.is_admin:
1310+ tenant_limit = cfg.CONF.max_software_deployments_per_tenant
1311+ count_all = self.software_config.count_software_deployment(cnxt)
1312+ if (tenant_limit >= 0 and
1313+ count_all >= tenant_limit):
1314+ message = _("You have reached the maximum software "
1315+ "deployments per tenant, %d. "
1316+ "Please delete some deployments.") % tenant_limit
1317+ raise exception.RequestLimitExceeded(message=message)
1318 return self.software_config.create_software_deployment(
1319 cnxt, server_id=server_id,
1320 config_id=config_id,
1321diff --git a/heat/engine/service_software_config.py b/heat/engine/service_software_config.py
1322index 1c1fcb4..ce5001b 100644
1323--- a/heat/engine/service_software_config.py
1324+++ b/heat/engine/service_software_config.py
1325@@ -52,6 +52,9 @@ class SoftwareConfigService(object):
1326 for sc in scs]
1327 return result
1328
1329+ def count_software_config(self, cnxt):
1330+ return software_config_object.SoftwareConfig.count_all(cnxt)
1331+
1332 def create_software_config(self, cnxt, group, name, config,
1333 inputs, outputs, options):
1334
1335@@ -81,6 +84,10 @@ class SoftwareConfigService(object):
1336 result = [api.format_software_deployment(sd) for sd in all_sd]
1337 return result
1338
1339+ def count_software_deployment(self, cnxt):
1340+ return software_deployment_object.SoftwareDeployment.count_all(
1341+ cnxt)
1342+
1343 def metadata_software_deployments(self, cnxt, server_id):
1344 if not server_id:
1345 raise ValueError(_('server_id must be specified'))
1346diff --git a/heat/engine/stack.py b/heat/engine/stack.py
1347index c7ebec1..caad797 100644
1348--- a/heat/engine/stack.py
1349+++ b/heat/engine/stack.py
1350@@ -1343,7 +1343,7 @@ class Stack(collections.abc.Mapping):
1351 elif create_if_missing:
1352 kwargs = self.get_kwargs_for_cloning(keep_tags=True)
1353 kwargs['owner_id'] = self.id
1354- del(kwargs['prev_raw_template_id'])
1355+ del kwargs['prev_raw_template_id']
1356 prev = type(self)(self.context, self._backup_name(),
1357 copy.deepcopy(self.t),
1358 **kwargs)
1359@@ -2132,7 +2132,8 @@ class Stack(collections.abc.Mapping):
1360
1361 def delete_all_snapshots(self):
1362 """Remove all snapshots for this stack."""
1363- snapshots = snapshot_object.Snapshot.get_all(self.context, self.id)
1364+ snapshots = snapshot_object.Snapshot.get_all_by_stack(
1365+ self.context, self.id)
1366 for snapshot in snapshots:
1367 self.delete_snapshot(snapshot)
1368 snapshot_object.Snapshot.delete(self.context, snapshot.id)
1369diff --git a/heat/hacking/checks.py b/heat/hacking/checks.py
1370index 4c39435..dcce567 100644
1371--- a/heat/hacking/checks.py
1372+++ b/heat/hacking/checks.py
1373@@ -26,7 +26,7 @@ Guidelines for writing new hacking checks
1374 - Pick numbers in the range H3xx. Find the current test with
1375 the highest allocated number and then pick the next value.
1376 - Keep the test method code in the source file ordered based
1377- on the Heat3xx value.
1378+ on the HE3xx value.
1379 - List the new rule in the top level HACKING.rst file
1380 - Add test cases for each new rule to heat/tests/test_hacking.py
1381
1382@@ -39,31 +39,31 @@ def no_log_warn(logical_line):
1383
1384 https://bugs.launchpad.net/tempest/+bug/1508442
1385
1386- Heat301
1387+ HE301
1388 """
1389 if logical_line.startswith('LOG.warn('):
1390- yield(0, 'Heat301 Use LOG.warning() rather than LOG.warn()')
1391+ yield (0, 'HE301 Use LOG.warning() rather than LOG.warn()')
1392
1393
1394 @core.flake8ext
1395 def check_python3_no_iteritems(logical_line):
1396- msg = ("Heat302: Use dict.items() instead of dict.iteritems().")
1397+ msg = ("HE302: Use dict.items() instead of dict.iteritems().")
1398
1399 if re.search(r".*\.iteritems\(\)", logical_line):
1400- yield(0, msg)
1401+ yield (0, msg)
1402
1403
1404 @core.flake8ext
1405 def check_python3_no_iterkeys(logical_line):
1406- msg = ("Heat303: Use dict.keys() instead of dict.iterkeys().")
1407+ msg = ("HE303: Use dict.keys() instead of dict.iterkeys().")
1408
1409 if re.search(r".*\.iterkeys\(\)", logical_line):
1410- yield(0, msg)
1411+ yield (0, msg)
1412
1413
1414 @core.flake8ext
1415 def check_python3_no_itervalues(logical_line):
1416- msg = ("Heat304: Use dict.values() instead of dict.itervalues().")
1417+ msg = ("HE304: Use dict.values() instead of dict.itervalues().")
1418
1419 if re.search(r".*\.itervalues\(\)", logical_line):
1420- yield(0, msg)
1421+ yield (0, msg)
1422diff --git a/heat/objects/snapshot.py b/heat/objects/snapshot.py
1423index 23d569a..8cd53aa 100644
1424--- a/heat/objects/snapshot.py
1425+++ b/heat/objects/snapshot.py
1426@@ -70,6 +70,11 @@ class Snapshot(
1427 db_api.snapshot_delete(context, snapshot_id)
1428
1429 @classmethod
1430- def get_all(cls, context, stack_id):
1431+ def get_all_by_stack(cls, context, stack_id):
1432 return [cls._from_db_object(context, cls(), db_snapshot)
1433- for db_snapshot in db_api.snapshot_get_all(context, stack_id)]
1434+ for db_snapshot
1435+ in db_api.snapshot_get_all_by_stack(context, stack_id)]
1436+
1437+ @classmethod
1438+ def count_all_by_stack(cls, context, stack_id):
1439+ return db_api.snapshot_count_all_by_stack(context, stack_id)
1440diff --git a/heat/objects/software_config.py b/heat/objects/software_config.py
1441index e0d9d42..df2cafb 100644
1442--- a/heat/objects/software_config.py
1443+++ b/heat/objects/software_config.py
1444@@ -66,5 +66,9 @@ class SoftwareConfig(
1445 return [cls._from_db_object(context, cls(), sc) for sc in scs]
1446
1447 @classmethod
1448+ def count_all(cls, context, **kwargs):
1449+ return db_api.software_config_count_all(context, **kwargs)
1450+
1451+ @classmethod
1452 def delete(cls, context, config_id):
1453 db_api.software_config_delete(context, config_id)
1454diff --git a/heat/objects/software_deployment.py b/heat/objects/software_deployment.py
1455index 564a484..d7cbc65 100644
1456--- a/heat/objects/software_deployment.py
1457+++ b/heat/objects/software_deployment.py
1458@@ -78,6 +78,11 @@ class SoftwareDeployment(
1459 context, server_id)]
1460
1461 @classmethod
1462+ def count_all(cls, context):
1463+ return db_api.software_deployment_count_all(
1464+ context)
1465+
1466+ @classmethod
1467 def update_by_id(cls, context, deployment_id, values):
1468 """Note this is a bit unusual as it returns the object.
1469
1470diff --git a/heat/tests/api/cfn/test_api_cfn_v1.py b/heat/tests/api/cfn/test_api_cfn_v1.py
1471index 88d3764..f078521 100644
1472--- a/heat/tests/api/cfn/test_api_cfn_v1.py
1473+++ b/heat/tests/api/cfn/test_api_cfn_v1.py
1474@@ -266,8 +266,8 @@ class CfnStackControllerTest(common.HeatTestCase):
1475 u'stack_status': u'COMPLETE',
1476 u'description': u'blah',
1477 u'disable_rollback': 'true',
1478- u'timeout_mins':60,
1479- u'capabilities':[]}]
1480+ u'timeout_mins': 60,
1481+ u'capabilities': []}]
1482
1483 self.m_call.side_effect = [identity, engine_resp]
1484
1485@@ -358,8 +358,8 @@ class CfnStackControllerTest(common.HeatTestCase):
1486 u'stack_status': u'COMPLETE',
1487 u'description': u'blah',
1488 u'disable_rollback': 'true',
1489- u'timeout_mins':60,
1490- u'capabilities':[]}]
1491+ u'timeout_mins': 60,
1492+ u'capabilities': []}]
1493
1494 self.m_call.return_value = engine_resp
1495
1496diff --git a/heat/tests/api/openstack_v1/test_routes.py b/heat/tests/api/openstack_v1/test_routes.py
1497index 29d1ae4..e929423 100644
1498--- a/heat/tests/api/openstack_v1/test_routes.py
1499+++ b/heat/tests/api/openstack_v1/test_routes.py
1500@@ -28,8 +28,8 @@ class RoutesTest(common.HeatTestCase):
1501 class_name = reflection.get_class_name(route['controller'].controller,
1502 fully_qualified=False)
1503 self.assertEqual(controller, class_name)
1504- del(route['action'])
1505- del(route['controller'])
1506+ del route['action']
1507+ del route['controller']
1508 self.assertEqual(params, route)
1509
1510 def setUp(self):
1511diff --git a/heat/tests/api/openstack_v1/test_stacks.py b/heat/tests/api/openstack_v1/test_stacks.py
1512index ff0a575..da92d27 100644
1513--- a/heat/tests/api/openstack_v1/test_stacks.py
1514+++ b/heat/tests/api/openstack_v1/test_stacks.py
1515@@ -1608,7 +1608,7 @@ class StackControllerTest(tools.ControllerTest, common.HeatTestCase):
1516 u'stack_status': u'COMPLETE',
1517 u'description': u'blah',
1518 u'disable_rollback': True,
1519- u'timeout_mins':60,
1520+ u'timeout_mins': 60,
1521 u'capabilities': [],
1522 }
1523 ]
1524@@ -1674,7 +1674,7 @@ class StackControllerTest(tools.ControllerTest, common.HeatTestCase):
1525 u'stack_status': u'COMPLETE',
1526 u'description': u'blah',
1527 u'disable_rollback': True,
1528- u'timeout_mins':60,
1529+ u'timeout_mins': 60,
1530 u'capabilities': [],
1531 }
1532 ]
1533diff --git a/heat/tests/clients/test_swift_client.py b/heat/tests/clients/test_swift_client.py
1534index ae04ae4..8ad43f7 100644
1535--- a/heat/tests/clients/test_swift_client.py
1536+++ b/heat/tests/clients/test_swift_client.py
1537@@ -14,7 +14,13 @@
1538 import datetime
1539 from unittest import mock
1540
1541-import pytz
1542+try:
1543+ import zoneinfo
1544+except ImportError:
1545+ # zoneinfo is available in Python >= 3.9
1546+ import pytz
1547+ zoneinfo = None
1548+
1549 from testtools import matchers
1550
1551 from heat.engine.clients.os import swift
1552@@ -126,8 +132,12 @@ class SwiftUtilsTest(SwiftClientPluginTestCase):
1553
1554 def test_parse_last_modified(self):
1555 self.assertIsNone(self.swift_plugin.parse_last_modified(None))
1556+ if zoneinfo:
1557+ tz = zoneinfo.ZoneInfo('GMT')
1558+ else:
1559+ tz = pytz.timezone('GMT')
1560 now = datetime.datetime(
1561- 2015, 2, 5, 1, 4, 40, 0, pytz.timezone('GMT'))
1562+ 2015, 2, 5, 1, 4, 40, 0, tz)
1563 now_naive = datetime.datetime(
1564 2015, 2, 5, 1, 4, 40, 0)
1565 last_modified = now.strftime('%a, %d %b %Y %H:%M:%S %Z')
1566diff --git a/heat/tests/constraints/test_common_constraints.py b/heat/tests/constraints/test_common_constraints.py
1567index c466b06..03399bf 100644
1568--- a/heat/tests/constraints/test_common_constraints.py
1569+++ b/heat/tests/constraints/test_common_constraints.py
1570@@ -13,6 +13,12 @@
1571
1572 from unittest import mock
1573
1574+try:
1575+ import zoneinfo
1576+except ImportError:
1577+ # zoneinfo is available in Python >= 3.9
1578+ zoneinfo = None
1579+
1580 from heat.engine.constraint import common_constraints as cc
1581 from heat.tests import common
1582 from heat.tests import utils
1583@@ -279,7 +285,10 @@ class TimezoneConstraintTest(common.HeatTestCase):
1584
1585 def test_validation_error(self):
1586 timezone = "wrong_timezone"
1587- expected = "Invalid timezone: '%s'" % timezone
1588+ err = timezone
1589+ if zoneinfo:
1590+ err = "No time zone found with key %s" % timezone
1591+ expected = "Invalid timezone: '%s'" % err
1592
1593 self.assertFalse(self.constraint.validate(timezone, self.ctx))
1594 self.assertEqual(
1595diff --git a/heat/tests/convergence/framework/engine_wrapper.py b/heat/tests/convergence/framework/engine_wrapper.py
1596index c1fb47c..062229c 100644
1597--- a/heat/tests/convergence/framework/engine_wrapper.py
1598+++ b/heat/tests/convergence/framework/engine_wrapper.py
1599@@ -54,13 +54,13 @@ class Engine(message_processor.MessageProcessor):
1600 if props:
1601 props_def = {}
1602 for prop_name, prop_value in props.items():
1603- if type(prop_value) == scenario_template.GetRes:
1604- prop_res = getattr(prop_value, "target_name")
1605- prop_value = {'get_resource': prop_res}
1606- elif type(prop_value) == scenario_template.GetAtt:
1607+ if isinstance(prop_value, scenario_template.GetAtt):
1608 prop_res = getattr(prop_value, "target_name")
1609 prop_attr = getattr(prop_value, "attr")
1610 prop_value = {'get_attr': [prop_res, prop_attr]}
1611+ elif isinstance(prop_value, scenario_template.GetRes):
1612+ prop_res = getattr(prop_value, "target_name")
1613+ prop_value = {'get_resource': prop_res}
1614 props_def[prop_name] = prop_value
1615 res_defn["properties"] = props_def
1616 if depends:
1617diff --git a/heat/tests/db/test_sqlalchemy_api.py b/heat/tests/db/test_sqlalchemy_api.py
1618index 5689f2e..26166a9 100644
1619--- a/heat/tests/db/test_sqlalchemy_api.py
1620+++ b/heat/tests/db/test_sqlalchemy_api.py
1621@@ -1121,6 +1121,13 @@ class SqlAlchemyTest(common.HeatTestCase):
1622 tenant_id='admin_tenant')
1623 self._test_software_config_get_all(get_ctx=admin_ctx)
1624
1625+ def test_software_config_count_all(self):
1626+ self.assertEqual(0, db_api.software_config_count_all(self.ctx))
1627+ self._create_software_config_record()
1628+ self._create_software_config_record()
1629+ self._create_software_config_record()
1630+ self.assertEqual(3, db_api.software_config_count_all(self.ctx))
1631+
1632 def test_software_config_delete(self):
1633 scf_id = self._create_software_config_record()
1634
1635@@ -1250,6 +1257,17 @@ class SqlAlchemyTest(common.HeatTestCase):
1636 deployments = db_api.software_deployment_get_all(admin_ctx)
1637 self.assertEqual(1, len(deployments))
1638
1639+ def test_software_deployment_count_all(self):
1640+ self.assertEqual(0, db_api.software_deployment_count_all(self.ctx))
1641+ values = self._deployment_values()
1642+ deployment = db_api.software_deployment_create(self.ctx, values)
1643+ self.assertIsNotNone(deployment)
1644+ deployment = db_api.software_deployment_create(self.ctx, values)
1645+ self.assertIsNotNone(deployment)
1646+ deployment = db_api.software_deployment_create(self.ctx, values)
1647+ self.assertIsNotNone(deployment)
1648+ self.assertEqual(3, db_api.software_deployment_count_all(self.ctx))
1649+
1650 def test_software_deployment_update(self):
1651 deployment_id = str(uuid.uuid4())
1652 err = self.assertRaises(exception.NotFound,
1653@@ -1421,7 +1439,7 @@ class SqlAlchemyTest(common.HeatTestCase):
1654
1655 self.assertIn(snapshot_id, str(err))
1656
1657- def test_snapshot_get_all(self):
1658+ def test_snapshot_get_all_by_stack(self):
1659 template = create_raw_template(self.ctx)
1660 user_creds = create_user_creds(self.ctx)
1661 stack = create_stack(self.ctx, template, user_creds)
1662@@ -1429,12 +1447,44 @@ class SqlAlchemyTest(common.HeatTestCase):
1663 'stack_id': stack.id}
1664 snapshot = db_api.snapshot_create(self.ctx, values)
1665 self.assertIsNotNone(snapshot)
1666- [snapshot] = db_api.snapshot_get_all(self.ctx, stack.id)
1667+ [snapshot] = db_api.snapshot_get_all_by_stack(self.ctx, stack.id)
1668 self.assertIsNotNone(snapshot)
1669 self.assertEqual(values['tenant'], snapshot.tenant)
1670 self.assertEqual(values['status'], snapshot.status)
1671 self.assertIsNotNone(snapshot.created_at)
1672
1673+ def test_snapshot_count_all_by_stack(self):
1674+ template = create_raw_template(self.ctx)
1675+ user_creds = create_user_creds(self.ctx)
1676+ stack1 = create_stack(self.ctx, template, user_creds)
1677+ stack2 = create_stack(self.ctx, template, user_creds)
1678+ values = [
1679+ {
1680+ 'tenant': self.ctx.tenant_id,
1681+ 'status': 'IN_PROGRESS',
1682+ 'stack_id': stack1.id,
1683+ 'name': 'snp1'
1684+ },
1685+ {
1686+ 'tenant': self.ctx.tenant_id,
1687+ 'status': 'IN_PROGRESS',
1688+ 'stack_id': stack1.id,
1689+ 'name': 'snp1'
1690+ },
1691+ {
1692+ 'tenant': self.ctx.tenant_id,
1693+ 'status': 'IN_PROGRESS',
1694+ 'stack_id': stack2.id,
1695+ 'name': 'snp2'
1696+ }
1697+ ]
1698+ for val in values:
1699+ self.assertIsNotNone(db_api.snapshot_create(self.ctx, val))
1700+ self.assertEqual(2, db_api.snapshot_count_all_by_stack(self.ctx,
1701+ stack1.id))
1702+ self.assertEqual(1, db_api.snapshot_count_all_by_stack(self.ctx,
1703+ stack2.id))
1704+
1705
1706 def create_raw_template(context, **kwargs):
1707 t = template_format.parse(wp_template)
1708@@ -2493,13 +2543,13 @@ class DBAPIResourceTest(common.HeatTestCase):
1709 [self.assertIn(val['name'], names) for val in values]
1710
1711 def test_resource_get_all_by_stack(self):
1712- self.stack1 = create_stack(self.ctx, self.template, self.user_creds)
1713- self.stack2 = create_stack(self.ctx, self.template, self.user_creds)
1714+ stack1 = create_stack(self.ctx, self.template, self.user_creds)
1715+ stack2 = create_stack(self.ctx, self.template, self.user_creds)
1716 values = [
1717 {'name': 'res1', 'stack_id': self.stack.id},
1718 {'name': 'res2', 'stack_id': self.stack.id},
1719 {'name': 'res3', 'stack_id': self.stack.id},
1720- {'name': 'res4', 'stack_id': self.stack1.id},
1721+ {'name': 'res4', 'stack_id': stack1.id},
1722 ]
1723 [create_resource(self.ctx, self.stack, False, **val)
1724 for val in values]
1725@@ -2530,7 +2580,7 @@ class DBAPIResourceTest(common.HeatTestCase):
1726 self.assertEqual('res2', resources.get('res2').name)
1727
1728 self.assertEqual({}, db_api.resource_get_all_by_stack(
1729- self.ctx, self.stack2.id))
1730+ self.ctx, stack2.id))
1731
1732 def test_resource_get_all_active_by_stack(self):
1733 values = [
1734@@ -2555,8 +2605,8 @@ class DBAPIResourceTest(common.HeatTestCase):
1735 self.assertIn(res.name, ['res2', 'res3', 'res4', 'res5', 'res6'])
1736
1737 def test_resource_get_all_by_root_stack(self):
1738- self.stack1 = create_stack(self.ctx, self.template, self.user_creds)
1739- self.stack2 = create_stack(self.ctx, self.template, self.user_creds)
1740+ stack1 = create_stack(self.ctx, self.template, self.user_creds)
1741+ stack2 = create_stack(self.ctx, self.template, self.user_creds)
1742
1743 create_resource(self.ctx, self.stack, name='res1',
1744 root_stack_id=self.stack.id)
1745@@ -2564,7 +2614,7 @@ class DBAPIResourceTest(common.HeatTestCase):
1746 root_stack_id=self.stack.id)
1747 create_resource(self.ctx, self.stack, name='res3',
1748 root_stack_id=self.stack.id)
1749- create_resource(self.ctx, self.stack1, name='res4',
1750+ create_resource(self.ctx, stack1, name='res4',
1751 root_stack_id=self.stack.id)
1752
1753 # Test for all resources in a stack
1754@@ -2596,7 +2646,7 @@ class DBAPIResourceTest(common.HeatTestCase):
1755 sorted(resource_names))
1756
1757 self.assertEqual({}, db_api.resource_get_all_by_root_stack(
1758- self.ctx, self.stack2.id))
1759+ self.ctx, stack2.id))
1760
1761 def test_resource_purge_deleted_by_stack(self):
1762 val = {'name': 'res1', 'action': rsrc.Resource.DELETE,
1763@@ -2944,14 +2994,14 @@ class DBAPIEventTest(common.HeatTestCase):
1764 self.assertEqual({'foo2': 'ev_bar'}, ret_event.rsrc_prop_data.data)
1765
1766 def test_event_get_all_by_tenant(self):
1767- self.stack1 = create_stack(self.ctx, self.template, self.user_creds,
1768- tenant='tenant1')
1769- self.stack2 = create_stack(self.ctx, self.template, self.user_creds,
1770- tenant='tenant2')
1771+ stack1 = create_stack(self.ctx, self.template, self.user_creds,
1772+ tenant='tenant1')
1773+ stack2 = create_stack(self.ctx, self.template, self.user_creds,
1774+ tenant='tenant2')
1775 values = [
1776- {'stack_id': self.stack1.id, 'resource_name': 'res1'},
1777- {'stack_id': self.stack1.id, 'resource_name': 'res2'},
1778- {'stack_id': self.stack2.id, 'resource_name': 'res3'},
1779+ {'stack_id': stack1.id, 'resource_name': 'res1'},
1780+ {'stack_id': stack1.id, 'resource_name': 'res2'},
1781+ {'stack_id': stack2.id, 'resource_name': 'res3'},
1782 ]
1783 [create_event(self.ctx, **val) for val in values]
1784
1785@@ -2984,38 +3034,38 @@ class DBAPIEventTest(common.HeatTestCase):
1786 self.assertEqual(1, len(events))
1787
1788 def test_event_get_all_by_stack(self):
1789- self.stack1 = create_stack(self.ctx, self.template, self.user_creds)
1790- self.stack2 = create_stack(self.ctx, self.template, self.user_creds)
1791+ stack1 = create_stack(self.ctx, self.template, self.user_creds)
1792+ stack2 = create_stack(self.ctx, self.template, self.user_creds)
1793 values = [
1794- {'stack_id': self.stack1.id, 'resource_name': 'res1'},
1795- {'stack_id': self.stack1.id, 'resource_name': 'res2'},
1796- {'stack_id': self.stack2.id, 'resource_name': 'res3'},
1797+ {'stack_id': stack1.id, 'resource_name': 'res1'},
1798+ {'stack_id': stack1.id, 'resource_name': 'res2'},
1799+ {'stack_id': stack2.id, 'resource_name': 'res3'},
1800 ]
1801 [create_event(self.ctx, **val) for val in values]
1802
1803 self.ctx.project_id = 'tenant1'
1804- events = db_api.event_get_all_by_stack(self.ctx, self.stack1.id)
1805+ events = db_api.event_get_all_by_stack(self.ctx, stack1.id)
1806 self.assertEqual(2, len(events))
1807
1808 self.ctx.project_id = 'tenant2'
1809- events = db_api.event_get_all_by_stack(self.ctx, self.stack2.id)
1810+ events = db_api.event_get_all_by_stack(self.ctx, stack2.id)
1811 self.assertEqual(1, len(events))
1812
1813 def test_event_count_all_by_stack(self):
1814- self.stack1 = create_stack(self.ctx, self.template, self.user_creds)
1815- self.stack2 = create_stack(self.ctx, self.template, self.user_creds)
1816+ stack1 = create_stack(self.ctx, self.template, self.user_creds)
1817+ stack2 = create_stack(self.ctx, self.template, self.user_creds)
1818 values = [
1819- {'stack_id': self.stack1.id, 'resource_name': 'res1'},
1820- {'stack_id': self.stack1.id, 'resource_name': 'res2'},
1821- {'stack_id': self.stack2.id, 'resource_name': 'res3'},
1822+ {'stack_id': stack1.id, 'resource_name': 'res1'},
1823+ {'stack_id': stack1.id, 'resource_name': 'res2'},
1824+ {'stack_id': stack2.id, 'resource_name': 'res3'},
1825 ]
1826 [create_event(self.ctx, **val) for val in values]
1827
1828 self.assertEqual(2, db_api.event_count_all_by_stack(self.ctx,
1829- self.stack1.id))
1830+ stack1.id))
1831
1832 self.assertEqual(1, db_api.event_count_all_by_stack(self.ctx,
1833- self.stack2.id))
1834+ stack2.id))
1835
1836
1837 class DBAPIServiceTest(common.HeatTestCase):
1838@@ -3440,9 +3490,9 @@ class ResetStackStatusTests(common.HeatTestCase):
1839 db_api.reset_stack_status(self.ctx, self.stack.id)
1840
1841 grandchild = db_api.stack_get(self.ctx, grandchild.id)
1842- self.stack = db_api.stack_get(self.ctx, self.stack.id)
1843+ stack = db_api.stack_get(self.ctx, self.stack.id)
1844 resource = db_api.resource_get(self.ctx, resource.id)
1845 self.assertEqual('FAILED', grandchild.status)
1846 self.assertEqual('FAILED', resource.status)
1847 self.assertIsNone(resource.engine_id)
1848- self.assertEqual('FAILED', self.stack.status)
1849+ self.assertEqual('FAILED', stack.status)
1850diff --git a/heat/tests/engine/service/test_software_config.py b/heat/tests/engine/service/test_software_config.py
1851index 470f3e9..90645c3 100644
1852--- a/heat/tests/engine/service/test_software_config.py
1853+++ b/heat/tests/engine/service/test_software_config.py
1854@@ -15,6 +15,7 @@ import datetime
1855 from unittest import mock
1856 import uuid
1857
1858+from oslo_config import cfg
1859 from oslo_messaging.rpc import dispatcher
1860 from oslo_serialization import jsonutils as json
1861 from oslo_utils import timeutils
1862@@ -169,6 +170,14 @@ class SoftwareConfigServiceTest(common.HeatTestCase):
1863 config['outputs'])
1864 self.assertEqual(kwargs['options'], config['options'])
1865
1866+ def test_create_config_exceeds_max_per_tenant(self):
1867+ cfg.CONF.set_override('max_software_configs_per_tenant', 0)
1868+ ex = self.assertRaises(dispatcher.ExpectedException,
1869+ self._create_software_config)
1870+ self.assertEqual(exception.RequestLimitExceeded, ex.exc_info[0])
1871+ self.assertIn("You have reached the maximum software configs "
1872+ "per tenant", str(ex.exc_info[1]))
1873+
1874 def test_create_software_config_structured(self):
1875 kwargs = {
1876 'group': 'json-file',
1877@@ -504,6 +513,14 @@ class SoftwareConfigServiceTest(common.HeatTestCase):
1878 self.assertEqual(deployment_id, deployment['id'])
1879 self.assertEqual(kwargs['input_values'], deployment['input_values'])
1880
1881+ def test_create_deployment_exceeds_max_per_tenant(self):
1882+ cfg.CONF.set_override('max_software_deployments_per_tenant', 0)
1883+ ex = self.assertRaises(dispatcher.ExpectedException,
1884+ self._create_software_deployment)
1885+ self.assertEqual(exception.RequestLimitExceeded, ex.exc_info[0])
1886+ self.assertIn("You have reached the maximum software deployments"
1887+ " per tenant", str(ex.exc_info[1]))
1888+
1889 def test_create_software_deployment_invalid_stack_user_project_id(self):
1890 sc_kwargs = {
1891 'group': 'Heat::Chef',
1892@@ -753,12 +770,12 @@ class SoftwareConfigServiceTest(common.HeatTestCase):
1893 }
1894 self.engine.software_config._push_metadata_software_deployments(
1895 self.ctx, '1234', None)
1896- res_upd.has_calls(
1897+ res_upd.assert_has_calls([
1898 mock.call(self.ctx, '1234',
1899 {'rsrc_metadata': result_metadata}, 1),
1900 mock.call(self.ctx, '1234',
1901- {'rsrc_metadata': result_metadata}, 2),
1902- )
1903+ {}, 2),
1904+ ])
1905
1906 put.assert_called_once_with(
1907 'http://192.168.2.2/foo/bar', json.dumps(result_metadata))
1908diff --git a/heat/tests/engine/service/test_stack_snapshot.py b/heat/tests/engine/service/test_stack_snapshot.py
1909index 57e2e26..2d0e0b4 100644
1910--- a/heat/tests/engine/service/test_stack_snapshot.py
1911+++ b/heat/tests/engine/service/test_stack_snapshot.py
1912@@ -99,6 +99,19 @@ class SnapshotServiceTest(common.HeatTestCase):
1913 mock_load.assert_called_once_with(self.ctx, stack=mock.ANY)
1914
1915 @mock.patch.object(stack.Stack, 'load')
1916+ def test_create_snapshot_exceeds_max_per_stack(self, mock_load):
1917+ stk = self._create_stack('stack_snapshot_exceeds_max')
1918+ mock_load.return_value = stk
1919+
1920+ cfg.CONF.set_override('max_snapshots_per_stack', 0)
1921+ ex = self.assertRaises(dispatcher.ExpectedException,
1922+ self.engine.stack_snapshot,
1923+ self.ctx, stk.identifier(), 'snap_none')
1924+ self.assertEqual(exception.RequestLimitExceeded, ex.exc_info[0])
1925+ self.assertIn("You have reached the maximum snapshots per stack",
1926+ str(ex.exc_info[1]))
1927+
1928+ @mock.patch.object(stack.Stack, 'load')
1929 def test_create_snapshot_action_in_progress(self, mock_load):
1930 stack_name = 'stack_snapshot_action_in_progress'
1931 stk = self._create_stack(stack_name)
1932diff --git a/heat/tests/engine/test_resource_type.py b/heat/tests/engine/test_resource_type.py
1933index eab8ea8..ff23e70 100644
1934--- a/heat/tests/engine/test_resource_type.py
1935+++ b/heat/tests/engine/test_resource_type.py
1936@@ -42,8 +42,7 @@ class ResourceTypeTest(common.HeatTestCase):
1937 mock_is_service_available):
1938 mock_is_service_available.return_value = (True, None)
1939 resources = self.eng.list_resource_types(self.ctx, "DEPRECATED")
1940- self.assertEqual(set(['OS::Aodh::Alarm']),
1941- set(resources))
1942+ self.assertIn('OS::Aodh::Alarm', resources)
1943
1944 @mock.patch.object(res.Resource, 'is_service_available')
1945 def test_list_resource_types_supported(self,
1946diff --git a/heat/tests/openstack/aodh/test_alarm.py b/heat/tests/openstack/aodh/test_alarm.py
1947index 48b756f..609cc9d 100644
1948--- a/heat/tests/openstack/aodh/test_alarm.py
1949+++ b/heat/tests/openstack/aodh/test_alarm.py
1950@@ -16,6 +16,12 @@ import copy
1951 import json
1952 from unittest import mock
1953
1954+try:
1955+ import zoneinfo
1956+except ImportError:
1957+ # zoneinfo is available in Python >= 3.9
1958+ zoneinfo = None
1959+
1960 from heat.common import exception
1961 from heat.common import template_format
1962 from heat.engine.clients.os import aodh
1963@@ -579,11 +585,14 @@ class AodhAlarmTest(common.HeatTestCase):
1964 exception.ResourceFailure,
1965 scheduler.TaskRunner(rsrc.update, snippet)
1966 )
1967+ err = timezone
1968+ if zoneinfo:
1969+ err = "No time zone found with key %s" % timezone
1970 self.assertEqual(
1971 "StackValidationFailed: resources.MEMAlarmHigh: Property error: "
1972 "Properties.time_constraints[0].timezone: Error "
1973 "validating value '%s': Invalid timezone: '%s'"
1974- % (timezone, timezone),
1975+ % (timezone, err),
1976 error.message)
1977
1978 def test_alarm_live_state(self):
1979diff --git a/heat/tests/openstack/heat/test_none_resource.py b/heat/tests/openstack/heat/test_none_resource.py
1980index 7344b36..28e0879 100644
1981--- a/heat/tests/openstack/heat/test_none_resource.py
1982+++ b/heat/tests/openstack/heat/test_none_resource.py
1983@@ -76,7 +76,7 @@ outputs:
1984 before_refid = self.rsrc.FnGetRefId()
1985 self.assertIsNotNone(before_refid)
1986 new_t = self.t.copy()
1987- del(new_t['resources']['none']['properties']['ignored'])
1988+ del new_t['resources']['none']['properties']['ignored']
1989 utils.update_stack(self.stack, new_t)
1990 self.assertEqual((self.stack.UPDATE, self.stack.COMPLETE),
1991 self.stack.state)
1992diff --git a/heat/tests/openstack/heat/test_remote_stack.py b/heat/tests/openstack/heat/test_remote_stack.py
1993index b5df410..bbfb0e3 100644
1994--- a/heat/tests/openstack/heat/test_remote_stack.py
1995+++ b/heat/tests/openstack/heat/test_remote_stack.py
1996@@ -368,7 +368,7 @@ class RemoteStackTest(tests_common.HeatTestCase):
1997 ca_cert=ca_cert)
1998 self.assertEqual(ca_cert, rsrc._cacert)
1999 self.assertEqual(ca_cert, rsrc.cacert)
2000- self.assertTrue('/tmp/' in rsrc._ssl_verify)
2001+ self.assertIn('/tmp/', rsrc._ssl_verify)
2002
2003 def test_create_with_insecure(self):
2004 rsrc = self._create_with_remote_credential(insecure=True)
2005diff --git a/heat/tests/openstack/heat/test_software_deployment.py b/heat/tests/openstack/heat/test_software_deployment.py
2006index 307ec57..db9cf50 100644
2007--- a/heat/tests/openstack/heat/test_software_deployment.py
2008+++ b/heat/tests/openstack/heat/test_software_deployment.py
2009@@ -1318,7 +1318,7 @@ class SoftwareDeploymentTest(common.HeatTestCase):
2010 mock.call('swift_signal_url')],
2011 self.deployment.data_delete.mock_calls)
2012
2013- del(dep_data['swift_signal_object_name'])
2014+ del dep_data['swift_signal_object_name']
2015 self.deployment.physical_resource_name = mock.Mock()
2016 self.deployment._delete_swift_signal_url()
2017 self.assertFalse(self.deployment.physical_resource_name.called)
2018diff --git a/heat/tests/openstack/neutron/test_neutron_floating_ip.py b/heat/tests/openstack/neutron/test_neutron_floating_ip.py
2019index 6915c10..2ffdf1d 100644
2020--- a/heat/tests/openstack/neutron/test_neutron_floating_ip.py
2021+++ b/heat/tests/openstack/neutron/test_neutron_floating_ip.py
2022@@ -156,10 +156,10 @@ class NeutronFloatingIPTest(common.HeatTestCase):
2023 raise ex
2024
2025 def find_network_port(self, value):
2026- return('9c1eb3fe-7bba-479d-bd43-fdb0bc7cd151')
2027+ return '9c1eb3fe-7bba-479d-bd43-fdb0bc7cd151'
2028
2029 def find_network_ip(self, value):
2030- return('477e8273-60a7-4c41-b683-1d497e53c384')
2031+ return '477e8273-60a7-4c41-b683-1d497e53c384'
2032
2033 self.ctx = utils.dummy_context()
2034 tpl = template_format.parse(neutron_floating_template)
2035@@ -694,7 +694,7 @@ class NeutronFloatingIPTest(common.HeatTestCase):
2036
2037 # test update FloatingIp with None port_id
2038 props = copy.deepcopy(fip.properties.data)
2039- del(props['port_id'])
2040+ del props['port_id']
2041 update_snippet = rsrc_defn.ResourceDefinition(fip.name, fip.type(),
2042 stack.t.parse(stack.defn,
2043 props))
2044diff --git a/heat/tests/test_common_context.py b/heat/tests/test_common_context.py
2045index 95a08ec..91401d2 100644
2046--- a/heat/tests/test_common_context.py
2047+++ b/heat/tests/test_common_context.py
2048@@ -125,7 +125,7 @@ class TestRequestContext(common.HeatTestCase):
2049 user_domain_id=ctx_origin.get('user_domain_id'),
2050 project_domain_id=ctx_origin.get('project_domain_id'))
2051 ctx_dict = ctx.to_dict()
2052- del(ctx_dict['request_id'])
2053+ del ctx_dict['request_id']
2054 self.assertEqual(ctx_origin, ctx_dict)
2055
2056 def test_request_context_from_dict(self):
2057diff --git a/heat/tests/test_convg_stack.py b/heat/tests/test_convg_stack.py
2058index c001af1..722da9a 100644
2059--- a/heat/tests/test_convg_stack.py
2060+++ b/heat/tests/test_convg_stack.py
2061@@ -549,14 +549,14 @@ class StackConvergenceCreateUpdateDeleteTest(common.HeatTestCase):
2062
2063 # Ensure that snapshot is not deleted on stack update
2064 stack.converge_stack(template=stack.t, action=stack.UPDATE)
2065- db_snapshot_obj = snapshot_objects.Snapshot.get_all(
2066+ db_snapshot_obj = snapshot_objects.Snapshot.get_all_by_stack(
2067 stack.context, stack.id)
2068 self.assertEqual('fake_snapshot', db_snapshot_obj[0].name)
2069 self.assertEqual(stack.id, db_snapshot_obj[0].stack_id)
2070
2071 # Ensure that snapshot is deleted on stack delete
2072 stack.converge_stack(template=stack.t, action=stack.DELETE)
2073- self.assertEqual([], snapshot_objects.Snapshot.get_all(
2074+ self.assertEqual([], snapshot_objects.Snapshot.get_all_by_stack(
2075 stack.context, stack.id))
2076 self.assertTrue(mock_cr.called)
2077
2078diff --git a/heat/tests/test_environment.py b/heat/tests/test_environment.py
2079index 6de00dc..de39e15 100644
2080--- a/heat/tests/test_environment.py
2081+++ b/heat/tests/test_environment.py
2082@@ -12,7 +12,6 @@
2083 # under the License.
2084
2085 import os.path
2086-import sys
2087 from unittest import mock
2088
2089 import fixtures
2090@@ -47,7 +46,7 @@ class EnvironmentTest(common.HeatTestCase):
2091 u'resource_registry': {u'resources': {}}}
2092 env = environment.Environment(old)
2093 self.assertEqual(expected, env.env_as_dict())
2094- del(expected['encrypted_param_names'])
2095+ del expected['encrypted_param_names']
2096 self.assertEqual(expected, env.user_env_as_dict())
2097
2098 def test_load_new_env(self):
2099@@ -59,7 +58,7 @@ class EnvironmentTest(common.HeatTestCase):
2100 u'resources': {}}}
2101 env = environment.Environment(new_env)
2102 self.assertEqual(new_env, env.env_as_dict())
2103- del(new_env['encrypted_param_names'])
2104+ del new_env['encrypted_param_names']
2105 self.assertEqual(new_env, env.user_env_as_dict())
2106
2107 def test_global_registry(self):
2108@@ -183,7 +182,6 @@ def constraint_mapping():
2109 plugin_file = os.path.join(plugin_dir.path, 'test.py')
2110 with open(plugin_file, 'w+') as ef:
2111 ef.write(constraint_content)
2112- self.addCleanup(sys.modules.pop, "heat.engine.plugins.test")
2113 cfg.CONF.set_override('plugin_dirs', plugin_dir.path)
2114
2115 env = environment.Environment({})
2116@@ -202,7 +200,6 @@ def constraint_mapping():
2117 plugin_file = os.path.join(plugin_dir.path, 'test.py')
2118 with open(plugin_file, 'w+') as ef:
2119 ef.write(constraint_content)
2120- self.addCleanup(sys.modules.pop, "heat.engine.plugins.test")
2121 cfg.CONF.set_override('plugin_dirs', plugin_dir.path)
2122
2123 env = environment.Environment({})
2124diff --git a/heat/tests/test_loguserdata.py b/heat/tests/test_loguserdata.py
2125index 7bb1700..09b214a 100644
2126--- a/heat/tests/test_loguserdata.py
2127+++ b/heat/tests/test_loguserdata.py
2128@@ -21,68 +21,8 @@ from heat.cloudinit import loguserdata
2129 from heat.tests import common
2130
2131
2132-class FakeCiVersion(object):
2133- def __init__(self, version):
2134- self.version = version
2135-
2136-
2137 class LoguserdataTest(common.HeatTestCase):
2138
2139- @mock.patch('pkg_resources.get_distribution')
2140- def test_ci_version_with_pkg_resources(self, mock_get):
2141- # Setup
2142- returned_versions = [
2143- FakeCiVersion('0.5.0'),
2144- FakeCiVersion('0.5.9'),
2145- FakeCiVersion('0.6.0'),
2146- FakeCiVersion('0.7.0'),
2147- FakeCiVersion('1.0'),
2148- FakeCiVersion('2.0'),
2149- ]
2150- mock_get.side_effect = returned_versions
2151-
2152- # Test & Verify
2153- self.assertFalse(loguserdata.chk_ci_version())
2154- self.assertFalse(loguserdata.chk_ci_version())
2155- self.assertTrue(loguserdata.chk_ci_version())
2156- self.assertTrue(loguserdata.chk_ci_version())
2157- self.assertTrue(loguserdata.chk_ci_version())
2158- self.assertTrue(loguserdata.chk_ci_version())
2159- self.assertEqual(6, mock_get.call_count)
2160-
2161- @mock.patch('pkg_resources.get_distribution')
2162- @mock.patch('subprocess.Popen')
2163- def test_ci_version_with_subprocess(self, mock_popen,
2164- mock_get_distribution):
2165- # Setup
2166- mock_get_distribution.side_effect = Exception()
2167-
2168- popen_return = [
2169- [None, 'cloud-init 0.0.5\n'],
2170- [None, 'cloud-init 0.7.5\n'],
2171- ]
2172- mock_popen.return_value = mock.MagicMock()
2173- mock_popen.return_value.communicate.side_effect = popen_return
2174-
2175- # Test & Verify
2176- self.assertFalse(loguserdata.chk_ci_version())
2177- self.assertTrue(loguserdata.chk_ci_version())
2178- self.assertEqual(2, mock_get_distribution.call_count)
2179-
2180- @mock.patch('pkg_resources.get_distribution')
2181- @mock.patch('subprocess.Popen')
2182- def test_ci_version_with_subprocess_exception(self, mock_popen,
2183- mock_get_distribution):
2184- # Setup
2185- mock_get_distribution.side_effect = Exception()
2186- mock_popen.return_value = mock.MagicMock()
2187- mock_popen.return_value.communicate.return_value = ['non-empty',
2188- 'irrelevant']
2189-
2190- # Test
2191- self.assertRaises(Exception, loguserdata.chk_ci_version) # noqa
2192- self.assertEqual(1, mock_get_distribution.call_count)
2193-
2194 @mock.patch('subprocess.Popen')
2195 def test_call(self, mock_popen):
2196 # Setup
2197@@ -141,12 +81,10 @@ class LoguserdataTest(common.HeatTestCase):
2198 # Verify
2199 self.assertEqual(os.EX_SOFTWARE, return_code)
2200
2201- @mock.patch('pkg_resources.get_distribution')
2202 @mock.patch('os.chmod')
2203 @mock.patch('heat.cloudinit.loguserdata.call')
2204- def test_main(self, mock_call, mock_chmod, mock_get):
2205+ def test_main(self, mock_call, mock_chmod):
2206 # Setup
2207- mock_get.return_value = FakeCiVersion('1.0')
2208 mock_call.return_value = 10
2209
2210 # Test
2211@@ -156,14 +94,3 @@ class LoguserdataTest(common.HeatTestCase):
2212 expected_path = os.path.join(loguserdata.VAR_PATH, 'cfn-userdata')
2213 mock_chmod.assert_called_once_with(expected_path, int('700', 8))
2214 self.assertEqual(10, return_code)
2215-
2216- @mock.patch('pkg_resources.get_distribution')
2217- def test_main_failed_ci_version(self, mock_get):
2218- # Setup
2219- mock_get.return_value = FakeCiVersion('0.0.0')
2220-
2221- # Test
2222- return_code = loguserdata.main()
2223-
2224- # Verify
2225- self.assertEqual(-1, return_code)
2226diff --git a/heat/tests/test_resource.py b/heat/tests/test_resource.py
2227index d360916..231d728 100644
2228--- a/heat/tests/test_resource.py
2229+++ b/heat/tests/test_resource.py
2230@@ -4092,8 +4092,8 @@ class ResourceAvailabilityTest(common.HeatTestCase):
2231 service_type='test_type',
2232 service_name=(generic_rsrc.ResourceWithDefaultClientName
2233 .default_client_name))
2234- mock_client_plugin.has_extension.has_calls(
2235- [('foo'), ('bar')])
2236+ mock_client_plugin.has_extension.assert_has_calls(
2237+ [mock.call('foo'), mock.call('bar')])
2238
2239 @mock.patch.object(clients.OpenStackClients, 'client_plugin')
2240 def test_service_deployed_required_extension_true_list(
2241@@ -4118,8 +4118,8 @@ class ResourceAvailabilityTest(common.HeatTestCase):
2242 service_type='test_type',
2243 service_name=(generic_rsrc.ResourceWithDefaultClientName
2244 .default_client_name))
2245- mock_client_plugin.has_extension.has_calls(
2246- [('foo'), ('bar')])
2247+ mock_client_plugin.has_extension.assert_has_calls(
2248+ [mock.call('foo'), mock.call('bar')])
2249
2250 @mock.patch.object(clients.OpenStackClients, 'client_plugin')
2251 def test_service_deployed_required_extension_true_list_fail(
2252@@ -4144,8 +4144,8 @@ class ResourceAvailabilityTest(common.HeatTestCase):
2253 service_type='test_type',
2254 service_name=(generic_rsrc.ResourceWithDefaultClientName
2255 .default_client_name))
2256- mock_client_plugin.has_extension.has_calls(
2257- [('foo'), ('bar')])
2258+ mock_client_plugin.has_extension.assert_has_calls(
2259+ [mock.call('foo'), mock.call('bar')])
2260
2261
2262 class TestLiveStateUpdate(common.HeatTestCase):
2263diff --git a/heat/tests/test_stack_delete.py b/heat/tests/test_stack_delete.py
2264index 51fb26a..d509ed2 100644
2265--- a/heat/tests/test_stack_delete.py
2266+++ b/heat/tests/test_stack_delete.py
2267@@ -73,7 +73,7 @@ class StackTest(common.HeatTestCase):
2268 }
2269 snapshot_object.Snapshot.create(self.ctx, snapshot_fake)
2270
2271- self.assertIsNotNone(snapshot_object.Snapshot.get_all(
2272+ self.assertIsNotNone(snapshot_object.Snapshot.get_all_by_stack(
2273 self.ctx, stack_id))
2274
2275 self.stack.delete()
2276@@ -81,7 +81,7 @@ class StackTest(common.HeatTestCase):
2277 self.assertIsNone(db_s)
2278 self.assertEqual((stack.Stack.DELETE, stack.Stack.COMPLETE),
2279 self.stack.state)
2280- self.assertEqual([], snapshot_object.Snapshot.get_all(
2281+ self.assertEqual([], snapshot_object.Snapshot.get_all_by_stack(
2282 self.ctx, stack_id))
2283
2284 def test_delete_with_snapshot_after_stack_add_resource(self):
2285@@ -104,7 +104,7 @@ class StackTest(common.HeatTestCase):
2286 }
2287 snapshot_object.Snapshot.create(self.ctx, snapshot_fake)
2288
2289- self.assertIsNotNone(snapshot_object.Snapshot.get_all(
2290+ self.assertIsNotNone(snapshot_object.Snapshot.get_all_by_stack(
2291 self.ctx, stack_id))
2292
2293 new_tmpl = {'heat_template_version': 'queens',
2294@@ -121,7 +121,7 @@ class StackTest(common.HeatTestCase):
2295 self.assertIsNone(db_s)
2296 self.assertEqual((stack.Stack.DELETE, stack.Stack.COMPLETE),
2297 self.stack.state)
2298- self.assertEqual([], snapshot_object.Snapshot.get_all(
2299+ self.assertEqual([], snapshot_object.Snapshot.get_all_by_stack(
2300 self.ctx, stack_id))
2301
2302 def test_delete_user_creds(self):
2303diff --git a/heat/tests/test_template_format.py b/heat/tests/test_template_format.py
2304index 3ce127c..aeb2368 100644
2305--- a/heat/tests/test_template_format.py
2306+++ b/heat/tests/test_template_format.py
2307@@ -56,12 +56,12 @@ class JsonToYamlTest(common.HeatTestCase):
2308
2309 self.assertEqual(u'2012-12-12', yml[u'HeatTemplateFormatVersion'])
2310 self.assertNotIn(u'AWSTemplateFormatVersion', yml)
2311- del(yml[u'HeatTemplateFormatVersion'])
2312+ del yml[u'HeatTemplateFormatVersion']
2313
2314 jsn = template_format.parse(json_str)
2315
2316 if u'AWSTemplateFormatVersion' in jsn:
2317- del(jsn[u'AWSTemplateFormatVersion'])
2318+ del jsn[u'AWSTemplateFormatVersion']
2319
2320 self.assertEqual(yml, jsn)
2321
2322@@ -189,7 +189,7 @@ class JsonYamlResolvedCompareTest(common.HeatTestCase):
2323 def compare_stacks(self, json_file, yaml_file, parameters):
2324 t1 = self.load_template(json_file)
2325 t2 = self.load_template(yaml_file)
2326- del(t1[u'AWSTemplateFormatVersion'])
2327+ del t1[u'AWSTemplateFormatVersion']
2328 t1[u'HeatTemplateFormatVersion'] = t2[u'HeatTemplateFormatVersion']
2329 stack1 = utils.parse_stack(t1, parameters)
2330 stack2 = utils.parse_stack(t2, parameters)
2331@@ -197,10 +197,10 @@ class JsonYamlResolvedCompareTest(common.HeatTestCase):
2332 # compare resources separately so that resolved static data
2333 # is compared
2334 t1nr = dict(stack1.t.t)
2335- del(t1nr['Resources'])
2336+ del t1nr['Resources']
2337
2338 t2nr = dict(stack2.t.t)
2339- del(t2nr['Resources'])
2340+ del t2nr['Resources']
2341 self.assertEqual(t1nr, t2nr)
2342
2343 self.assertEqual(set(stack1), set(stack2))
2344diff --git a/openstack_heat.egg-info/PKG-INFO b/openstack_heat.egg-info/PKG-INFO
2345index d68f1ed..9541147 100644
2346--- a/openstack_heat.egg-info/PKG-INFO
2347+++ b/openstack_heat.egg-info/PKG-INFO
2348@@ -1,10 +1,93 @@
2349-Metadata-Version: 2.1
2350+Metadata-Version: 1.2
2351 Name: openstack-heat
2352-Version: 21.1.0.dev70
2353+Version: 22.0.0.0rc1
2354 Summary: OpenStack Orchestration
2355 Home-page: https://docs.openstack.org/heat/latest/
2356 Author: OpenStack
2357 Author-email: openstack-discuss@lists.openstack.org
2358+License: UNKNOWN
2359+Description: ========================
2360+ Team and repository tags
2361+ ========================
2362+
2363+ .. image:: https://governance.openstack.org/tc/badges/heat.svg
2364+ :target: https://governance.openstack.org/tc/reference/tags/index.html
2365+
2366+ .. Change things from this point on
2367+
2368+ ====
2369+ Heat
2370+ ====
2371+
2372+ Heat is a service to orchestrate multiple composite cloud applications using
2373+ templates, through both an OpenStack-native REST API and a
2374+ CloudFormation-compatible Query API.
2375+
2376+ Why heat? It makes the clouds rise and keeps them there.
2377+
2378+ Getting Started
2379+ ---------------
2380+
2381+ If you'd like to run from the master branch, you can clone the git repo:
2382+
2383+ git clone https://opendev.org/openstack/heat
2384+
2385+
2386+ * Documentation: https://docs.openstack.org/heat/latest
2387+ * Template samples: https://opendev.org/openstack/heat-templates
2388+ * Agents: https://opendev.org/openstack/heat-agents
2389+ * Release Notes: https://docs.openstack.org/releasenotes/heat/
2390+
2391+ Python client
2392+ -------------
2393+
2394+ * Documentation: https://docs.openstack.org/python-heatclient/latest
2395+ * Source: https://opendev.org/openstack/python-heatclient
2396+
2397+ Report a Story (a bug/blueprint)
2398+ --------------------------------
2399+
2400+ If you'd like to report a Story (we used to call a bug/blueprint), you can
2401+ report it under Report a story in
2402+ `Heat's StoryBoard <https://storyboard.openstack.org/#!/project/989>`_.
2403+ If you must report the story under other sub-project of heat, you can find
2404+ them all in `Heat StoryBoard Group <https://storyboard.openstack.org/#!/project_group/82>`_.
2405+ if you encounter any issue.
2406+
2407+ References
2408+ ----------
2409+ * https://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_CreateStack.html
2410+ * https://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/create-stack.html
2411+ * https://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html
2412+ * https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=tosca
2413+
2414+ We have integration with
2415+ ------------------------
2416+ * https://opendev.org/openstack/python-novaclient (instance)
2417+ * https://opendev.org/openstack/python-keystoneclient (auth)
2418+ * https://opendev.org/openstack/python-swiftclient (object storage)
2419+ * https://opendev.org/openstack/python-neutronclient (networking)
2420+ * https://opendev.org/openstack/python-aodhclient (alarming service)
2421+ * https://opendev.org/openstack/python-cinderclient (block storage)
2422+ * https://opendev.org/openstack/python-glanceclient (image service)
2423+ * https://opendev.org/openstack/python-troveclient (database as a Service)
2424+ * https://opendev.org/openstack/python-saharaclient (hadoop cluster)
2425+ * https://opendev.org/openstack/python-barbicanclient (key management service)
2426+ * https://opendev.org/openstack/python-designateclient (DNS service)
2427+ * https://opendev.org/openstack/python-magnumclient (container service)
2428+ * https://opendev.org/openstack/python-manilaclient (shared file system service)
2429+ * https://opendev.org/openstack/python-mistralclient (workflow service)
2430+ * https://opendev.org/openstack/python-zaqarclient (messaging service)
2431+ * https://opendev.org/openstack/python-monascaclient (monitoring service)
2432+ * https://opendev.org/openstack/python-zunclient (container management service)
2433+ * https://opendev.org/openstack/python-blazarclient (reservation service)
2434+ * https://opendev.org/openstack/python-octaviaclient.git (Load-balancer service)
2435+ * https://opendev.org/openstack/python-senlinclient (Clustering service)
2436+ * https://opendev.org/openstack/python-vitrageclient.git (RCA service)
2437+ * https://opendev.org/openstack/python-ironicclient (baremetal provisioning service)
2438+
2439+
2440+Platform: UNKNOWN
2441 Classifier: Environment :: OpenStack
2442 Classifier: Intended Audience :: Information Technology
2443 Classifier: Intended Audience :: System Administrators
2444@@ -18,85 +101,3 @@ Classifier: Programming Language :: Python :: 3.9
2445 Classifier: Programming Language :: Python :: 3.10
2446 Classifier: Programming Language :: Python :: 3.11
2447 Requires-Python: >=3.8
2448-License-File: LICENSE
2449-
2450-========================
2451-Team and repository tags
2452-========================
2453-
2454-.. image:: https://governance.openstack.org/tc/badges/heat.svg
2455- :target: https://governance.openstack.org/tc/reference/tags/index.html
2456-
2457-.. Change things from this point on
2458-
2459-====
2460-Heat
2461-====
2462-
2463-Heat is a service to orchestrate multiple composite cloud applications using
2464-templates, through both an OpenStack-native REST API and a
2465-CloudFormation-compatible Query API.
2466-
2467-Why heat? It makes the clouds rise and keeps them there.
2468-
2469-Getting Started
2470----------------
2471-
2472-If you'd like to run from the master branch, you can clone the git repo:
2473-
2474- git clone https://opendev.org/openstack/heat
2475-
2476-
2477-* Documentation: https://docs.openstack.org/heat/latest
2478-* Template samples: https://opendev.org/openstack/heat-templates
2479-* Agents: https://opendev.org/openstack/heat-agents
2480-* Release Notes: https://docs.openstack.org/releasenotes/heat/
2481-
2482-Python client
2483--------------
2484-
2485-* Documentation: https://docs.openstack.org/python-heatclient/latest
2486-* Source: https://opendev.org/openstack/python-heatclient
2487-
2488-Report a Story (a bug/blueprint)
2489---------------------------------
2490-
2491-If you'd like to report a Story (we used to call a bug/blueprint), you can
2492-report it under Report a story in
2493-`Heat's StoryBoard <https://storyboard.openstack.org/#!/project/989>`_.
2494-If you must report the story under other sub-project of heat, you can find
2495-them all in `Heat StoryBoard Group <https://storyboard.openstack.org/#!/project_group/82>`_.
2496-if you encounter any issue.
2497-
2498-References
2499-----------
2500-* https://docs.amazonwebservices.com/AWSCloudFormation/latest/APIReference/API_CreateStack.html
2501-* https://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/create-stack.html
2502-* https://docs.amazonwebservices.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html
2503-* https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=tosca
2504-
2505-We have integration with
2506-------------------------
2507-* https://opendev.org/openstack/python-novaclient (instance)
2508-* https://opendev.org/openstack/python-keystoneclient (auth)
2509-* https://opendev.org/openstack/python-swiftclient (object storage)
2510-* https://opendev.org/openstack/python-neutronclient (networking)
2511-* https://opendev.org/openstack/python-aodhclient (alarming service)
2512-* https://opendev.org/openstack/python-cinderclient (block storage)
2513-* https://opendev.org/openstack/python-glanceclient (image service)
2514-* https://opendev.org/openstack/python-troveclient (database as a Service)
2515-* https://opendev.org/openstack/python-saharaclient (hadoop cluster)
2516-* https://opendev.org/openstack/python-barbicanclient (key management service)
2517-* https://opendev.org/openstack/python-designateclient (DNS service)
2518-* https://opendev.org/openstack/python-magnumclient (container service)
2519-* https://opendev.org/openstack/python-manilaclient (shared file system service)
2520-* https://opendev.org/openstack/python-mistralclient (workflow service)
2521-* https://opendev.org/openstack/python-zaqarclient (messaging service)
2522-* https://opendev.org/openstack/python-monascaclient (monitoring service)
2523-* https://opendev.org/openstack/python-zunclient (container management service)
2524-* https://opendev.org/openstack/python-blazarclient (reservation service)
2525-* https://opendev.org/openstack/python-octaviaclient.git (Load-balancer service)
2526-* https://opendev.org/openstack/python-senlinclient (Clustering service)
2527-* https://opendev.org/openstack/python-vitrageclient.git (RCA service)
2528-* https://opendev.org/openstack/python-ironicclient (baremetal provisioning service)
2529-
2530diff --git a/openstack_heat.egg-info/SOURCES.txt b/openstack_heat.egg-info/SOURCES.txt
2531index adfe427..04faf9d 100644
2532--- a/openstack_heat.egg-info/SOURCES.txt
2533+++ b/openstack_heat.egg-info/SOURCES.txt
2534@@ -1144,6 +1144,7 @@ releasenotes/notes/delay-resource-7d44c512081026c8.yaml
2535 releasenotes/notes/delay-resource-e20ba61f31799f6e.yaml
2536 releasenotes/notes/deployment-swift-data-server-property-51fd4f9d1671fc90.yaml
2537 releasenotes/notes/deprecate-heat-manage-migrate_properties_data-command-754c94ef87626a82.yaml
2538+releasenotes/notes/deprecate-inactive-project-rsc-caracal-ba0b0005f67580f9.yaml
2539 releasenotes/notes/deprecate-json-formatted-policy-file-ac2f98b31ece0baf.yaml
2540 releasenotes/notes/deprecate-nova-floatingip-resources-d5c9447a199be402.yaml
2541 releasenotes/notes/deprecate-nova-quota-injected_file-properties-6c6fd7f5231e4c40.yaml
2542@@ -1190,6 +1191,7 @@ releasenotes/notes/know-limit-releasenote-4d21fc4d91d136d9.yaml
2543 releasenotes/notes/lbaasv2-hidden-a8f82ddfdba911eb.yaml
2544 releasenotes/notes/legacy-client-races-ba7a60cef5ec1694.yaml
2545 releasenotes/notes/legacy-stack-user-id-cebbad8b0f2ed490.yaml
2546+releasenotes/notes/limit-resources-aeb2f24e705840de.yaml
2547 releasenotes/notes/magnum-resource-update-0f617eec45ef8ef7.yaml
2548 releasenotes/notes/make_url-function-d76737adb1e54801.yaml
2549 releasenotes/notes/manual-rollback-failed-replacement-08ebb9271617fe9d.yaml
2550diff --git a/openstack_heat.egg-info/entry_points.txt b/openstack_heat.egg-info/entry_points.txt
2551index 46a5aef..f3bb439 100644
2552--- a/openstack_heat.egg-info/entry_points.txt
2553+++ b/openstack_heat.egg-info/entry_points.txt
2554@@ -117,6 +117,9 @@ zaqar.queue = heat.engine.clients.os.zaqar:QueueConstraint
2555 [heat.event_sinks]
2556 zaqar-queue = heat.engine.clients.os.zaqar:ZaqarEventSink
2557
2558+[heat.stack_lifecycle_plugins]
2559+
2560+
2561 [heat.templates]
2562 AWSTemplateFormatVersion.2010-09-09 = heat.engine.cfn.template:CfnTemplate
2563 HeatTemplateFormatVersion.2012-12-12 = heat.engine.cfn.template:HeatTemplate
2564@@ -140,12 +143,14 @@ heat_template_version.wallaby = heat.engine.hot.template:HOTemplate20210416
2565
2566 [oslo.config.opts]
2567 heat.api.aws.ec2token = heat.api.aws.ec2token:list_opts
2568+heat.common.cache = heat.common.cache:list_opts
2569 heat.common.config = heat.common.config:list_opts
2570 heat.common.context = heat.common.context:list_opts
2571 heat.common.crypt = heat.common.crypt:list_opts
2572 heat.common.wsgi = heat.common.wsgi:list_opts
2573 heat.engine.clients = heat.engine.clients:list_opts
2574 heat.engine.clients.os.keystone.heat_keystoneclient = heat.engine.clients.os.keystone.heat_keystoneclient:list_opts
2575+heat.engine.hot.functions = heat.engine.hot.functions:list_opts
2576 heat.engine.notification = heat.engine.notification:list_opts
2577 heat.engine.resources = heat.engine.resources:list_opts
2578 heat_integrationtests.common.config = heat_integrationtests.common.config:list_opts
2579@@ -162,3 +167,4 @@ heat = heat.policies:list_rules
2580 [wsgi_scripts]
2581 heat-wsgi-api = heat.httpd.heat_api:init_application
2582 heat-wsgi-api-cfn = heat.httpd.heat_api_cfn:init_application
2583+
2584diff --git a/openstack_heat.egg-info/pbr.json b/openstack_heat.egg-info/pbr.json
2585index a9140ec..3a193c6 100644
2586--- a/openstack_heat.egg-info/pbr.json
2587+++ b/openstack_heat.egg-info/pbr.json
2588@@ -1 +1 @@
2589-{"git_version": "2df46d4c5", "is_release": false}
2590\ No newline at end of file
2591+{"git_version": "8892f32a2", "is_release": true}
2592\ No newline at end of file
2593diff --git a/openstack_heat.egg-info/requires.txt b/openstack_heat.egg-info/requires.txt
2594index 0f71ad6..481fafb 100644
2595--- a/openstack_heat.egg-info/requires.txt
2596+++ b/openstack_heat.egg-info/requires.txt
2597@@ -55,8 +55,13 @@ python-troveclient>=2.2.0
2598 python-vitrageclient>=2.7.0
2599 python-zaqarclient>=1.3.0
2600 python-zunclient>=3.4.0
2601-pytz>=2013.6
2602 requests>=2.23.0
2603 stevedore>=3.1.0
2604 tenacity>=6.1.0
2605 yaql>=1.1.3
2606+
2607+[:(python_version<"3.9")]
2608+pytz>=2013.6
2609+
2610+[:(python_version>="3.9")]
2611+tzdata>=2022.4
2612diff --git a/releasenotes/config.yaml b/releasenotes/config.yaml
2613index 225dc0e..d72ed66 100644
2614--- a/releasenotes/config.yaml
2615+++ b/releasenotes/config.yaml
2616@@ -1,4 +1,4 @@
2617 ---
2618 branch_name_re: '^stable/\w+$'
2619 release_tag_re: '((?:[\d.ab]|rc)+)'
2620-closed_branch_tag_re: '^(\w+)-eol$'
2621+closed_branch_tag_re: '^(\w+)-eo[lm]$'
2622diff --git a/releasenotes/notes/deprecate-inactive-project-rsc-caracal-ba0b0005f67580f9.yaml b/releasenotes/notes/deprecate-inactive-project-rsc-caracal-ba0b0005f67580f9.yaml
2623new file mode 100644
2624index 0000000..edfa948
2625--- /dev/null
2626+++ b/releasenotes/notes/deprecate-inactive-project-rsc-caracal-ba0b0005f67580f9.yaml
2627@@ -0,0 +1,10 @@
2628+---
2629+deprecations:
2630+ - |
2631+ The following resources have been deprecated, because monasca, sahara and
2632+ senlin were marked inactive and will not get deliverables for the 2024.1
2633+ release. These resources will be removed in ``23.0.0`` release.
2634+
2635+ - ``OS::Monasca::*``
2636+ - ``OS::Sahara::*``
2637+ - ``OS::Senlin::*``
2638diff --git a/releasenotes/notes/limit-resources-aeb2f24e705840de.yaml b/releasenotes/notes/limit-resources-aeb2f24e705840de.yaml
2639new file mode 100644
2640index 0000000..7e9a02b
2641--- /dev/null
2642+++ b/releasenotes/notes/limit-resources-aeb2f24e705840de.yaml
2643@@ -0,0 +1,26 @@
2644+---
2645+features:
2646+ - |
2647+ Heat now supports limiting number of software configs, software
2648+ deployments, stack snapshots which users can create, by the following
2649+ config options. These limits are not enforced for users with admin role.
2650+
2651+ - ``[DEFAULT] max_software_configis_per_tenant``
2652+ - ``[DEFAULT] max_software_deployments_per_tenant``
2653+ - ``[DEFAULT] max_snapshots_per_stack``
2654+
2655+upgrade:
2656+ - |
2657+ Now the following limits are enforced by default, unless a request user
2658+ has admin role.
2659+
2660+ - Maximum number of software configs per project is 4096
2661+ - Maximum number of software deployments per project is 4096
2662+ - Maximum number of stack snapshots per tenant is 32
2663+
2664+ Set the following options in case the limits should be increased. Limits
2665+ can be disabled by setting -1 to these options.
2666+
2667+ - ``[DEFAULT] max_software_configis_per_tenant``
2668+ - ``[DEFAULT] max_software_deployments_per_tenant``
2669+ - ``[DEFAULT] max_snapshots_per_stack``
2670diff --git a/releasenotes/source/victoria.rst b/releasenotes/source/victoria.rst
2671index 4efc7b6..8ce9334 100644
2672--- a/releasenotes/source/victoria.rst
2673+++ b/releasenotes/source/victoria.rst
2674@@ -3,4 +3,4 @@ Victoria Series Release Notes
2675 =============================
2676
2677 .. release-notes::
2678- :branch: stable/victoria
2679+ :branch: unmaintained/victoria
2680diff --git a/releasenotes/source/wallaby.rst b/releasenotes/source/wallaby.rst
2681index d77b565..bcf35c5 100644
2682--- a/releasenotes/source/wallaby.rst
2683+++ b/releasenotes/source/wallaby.rst
2684@@ -3,4 +3,4 @@ Wallaby Series Release Notes
2685 ============================
2686
2687 .. release-notes::
2688- :branch: stable/wallaby
2689+ :branch: unmaintained/wallaby
2690diff --git a/releasenotes/source/xena.rst b/releasenotes/source/xena.rst
2691index 1be85be..d19eda4 100644
2692--- a/releasenotes/source/xena.rst
2693+++ b/releasenotes/source/xena.rst
2694@@ -3,4 +3,4 @@ Xena Series Release Notes
2695 =========================
2696
2697 .. release-notes::
2698- :branch: stable/xena
2699+ :branch: unmaintained/xena
2700diff --git a/releasenotes/source/yoga.rst b/releasenotes/source/yoga.rst
2701index 7cd5e90..43cafde 100644
2702--- a/releasenotes/source/yoga.rst
2703+++ b/releasenotes/source/yoga.rst
2704@@ -3,4 +3,4 @@ Yoga Series Release Notes
2705 =========================
2706
2707 .. release-notes::
2708- :branch: stable/yoga
2709+ :branch: unmaintained/yoga
2710diff --git a/requirements.txt b/requirements.txt
2711index 04ebf4e..f25280d 100644
2712--- a/requirements.txt
2713+++ b/requirements.txt
2714@@ -51,10 +51,11 @@ python-troveclient>=2.2.0 # Apache-2.0
2715 python-vitrageclient>=2.7.0 # Apache-2.0
2716 python-zaqarclient>=1.3.0 # Apache-2.0
2717 python-zunclient>=3.4.0 # Apache-2.0
2718-pytz>=2013.6 # MIT
2719+pytz>=2013.6;python_version<"3.9" # MIT
2720 PyYAML>=5.1 # MIT
2721 requests>=2.23.0 # Apache-2.0
2722 tenacity>=6.1.0 # Apache-2.0
2723+tzdata>=2022.4;python_version>="3.9" # MIT
2724 Routes>=2.3.1 # MIT
2725 SQLAlchemy>=1.4.0 # MIT
2726 stevedore>=3.1.0 # Apache-2.0
2727diff --git a/setup.cfg b/setup.cfg
2728index b89a99c..8e8cf4e 100644
2729--- a/setup.cfg
2730+++ b/setup.cfg
2731@@ -47,10 +47,12 @@ wsgi_scripts =
2732 heat-wsgi-api = heat.httpd.heat_api:init_application
2733 heat-wsgi-api-cfn = heat.httpd.heat_api_cfn:init_application
2734 oslo.config.opts =
2735+ heat.common.cache = heat.common.cache:list_opts
2736 heat.common.config = heat.common.config:list_opts
2737 heat.common.context = heat.common.context:list_opts
2738 heat.common.crypt = heat.common.crypt:list_opts
2739 heat.engine.clients.os.keystone.heat_keystoneclient = heat.engine.clients.os.keystone.heat_keystoneclient:list_opts
2740+ heat.engine.hot.functions = heat.engine.hot.functions:list_opts
2741 heat.common.wsgi = heat.common.wsgi:list_opts
2742 heat.engine.clients = heat.engine.clients:list_opts
2743 heat.engine.notification = heat.engine.notification:list_opts
2744diff --git a/test-requirements.txt b/test-requirements.txt
2745index 10f00a0..bea7ec6 100644
2746--- a/test-requirements.txt
2747+++ b/test-requirements.txt
2748@@ -1,13 +1,5 @@
2749-# The order of packages is significant, because pip processes them in the order
2750-# of appearance. Changing the order has an impact on the overall integration
2751-# process, which may cause wedges in the gate later.
2752-
2753 # Hacking already pins down pep8, pyflakes and flake8
2754-hacking>=3.0.1,<3.1.0 # Apache-2.0
2755-# remove this pyflakes from here once you bump the
2756-# hacking to 3.2.0 or above. hacking 3.2.0 takes
2757-# care of pyflakes version compatibilty.
2758-pyflakes>=2.1.1
2759+hacking>=6.1.0,<6.2.0 # Apache-2.0
2760
2761 bandit!=1.6.0,>=1.1.0 # Apache-2.0
2762 coverage!=4.4,>=4.0 # Apache-2.0
2763diff --git a/tox.ini b/tox.ini
2764index 6cd81ae..7dbbd6c 100644
2765--- a/tox.ini
2766+++ b/tox.ini
2767@@ -101,7 +101,6 @@ deps =
2768 # The following bandit tests are being skipped:
2769 # B101: Test for use of assert
2770 # B104: Test for binding to all interfaces
2771-# B107: Test for use of hard-coded password argument defaults
2772 # B110: Try, Except, Pass detected.
2773 # B113: Requests call without timeout
2774 # B310: Audit url open for permitted schemes
2775@@ -112,7 +111,7 @@ deps =
2776 # B506: Test for use of yaml load
2777 # B603: Test for use of subprocess with shell equals true
2778 # B607: Test for starting a process with a partial path
2779-commands = bandit -r heat -x tests --skip B101,B104,B107,B110,B113,B310,B311,B404,B410,B504,B506,B603,B607
2780+commands = bandit -r heat -x tests --skip B101,B104,B110,B113,B310,B311,B404,B410,B504,B506,B603,B607
2781
2782 [flake8]
2783 show-source = true
2784@@ -132,10 +131,10 @@ import_exceptions = heat.common.i18n
2785
2786 [flake8:local-plugins]
2787 extension =
2788- Heat301 = checks:no_log_warn
2789- Heat302 = checks:check_python3_no_iteritems
2790- Heat303 = checks:check_python3_no_iterkeys
2791- Heat304 = checks:check_python3_no_itervalues
2792+ HE301 = checks:no_log_warn
2793+ HE302 = checks:check_python3_no_iteritems
2794+ HE303 = checks:check_python3_no_iterkeys
2795+ HE304 = checks:check_python3_no_itervalues
2796 paths = ./heat/hacking
2797
2798 [testenv:debug]

Subscribers

People subscribed via source and target branches