Compare commits

..

2 Commits

Author SHA1 Message Date
c32881a8fc Update TOX_CONSTRAINTS_FILE for stable/2024.2
Update the URL to the upper-constraints file to point to the redirect
rule on releases.openstack.org so that anyone working on this branch
will switch to the correct upper-constraints list automatically when
the requirements repository branches.

Until the requirements repository has as stable/2024.2 branch, tests will
continue to use the upper-constraints list on master.

Change-Id: Ia1d309bd006182fccde062d5f0785249d4ecfeed
2024-09-06 13:32:02 +00:00
db4f86ce28 Update .gitreview for stable/2024.2
Change-Id: I2bf294d5bf7fd7c0e3720261679271f3ba4655ab
2024-09-06 13:32:01 +00:00
19 changed files with 27 additions and 293 deletions

View File

@@ -2,3 +2,4 @@
host=review.opendev.org
port=29418
project=openstack/python-tackerclient.git
defaultbranch=stable/2024.2

View File

@@ -3,4 +3,4 @@
===========================
.. release-notes::
:branch: unmaintained/2023.1
:branch: stable/2023.1

View File

@@ -1,6 +0,0 @@
===========================
2024.2 Series Release Notes
===========================
.. release-notes::
:branch: stable/2024.2

View File

@@ -7,7 +7,6 @@ Contents:
:maxdepth: 2
unreleased
2024.2
2024.1
2023.2
2023.1

View File

@@ -3,4 +3,4 @@ Zed Series Release Notes
========================
.. release-notes::
:branch: unmaintained/zed
:branch: stable/zed

View File

@@ -7,9 +7,11 @@ iso8601>=0.1.11 # MIT
netaddr>=0.7.18 # BSD
requests>=2.14.2 # Apache-2.0
python-keystoneclient>=3.8.0 # Apache-2.0
simplejson>=3.5.1 # MIT
stevedore>=1.20.0 # Apache-2.0
Babel!=2.4.0,>=2.3.4 # BSD
oslo.i18n>=3.15.3 # Apache-2.0
osc-lib>=1.8.0 # Apache-2.0
oslo.log>=3.36.0 # Apache-2.0
oslo.utils>=3.40.0 # Apache-2.0
oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0

View File

@@ -14,7 +14,10 @@
# under the License.
#
import json
try:
import json
except ImportError:
import simplejson as json
import logging
import os
@@ -128,11 +131,7 @@ class HTTPClient(object):
content_type = kwargs.pop('content_type', None) or 'application/json'
headers = headers or {}
accept = kwargs.pop('accept', None)
if accept:
headers.setdefault('Accept', f'application/{accept}')
else:
headers.setdefault('Accept', content_type)
headers.setdefault('Accept', content_type)
if body:
headers.setdefault('Content-Type', content_type)
@@ -285,11 +284,7 @@ class SessionClient(adapter.Adapter):
content_type = kwargs.pop('content_type', None) or 'application/json'
headers = kwargs.setdefault('headers', {})
accept = kwargs.pop('accept', None)
if accept:
headers.setdefault('Accept', f'application/{accept}')
else:
headers.setdefault('Accept', content_type)
headers.setdefault('Accept', content_type)
try:
kwargs.setdefault('data', kwargs.pop('body'))

View File

@@ -21,6 +21,7 @@ import argparse
import logging
import os
from oslo_log import versionutils
from oslo_utils import encodeutils
from oslo_utils import importutils
@@ -176,3 +177,9 @@ def get_file_path(filename):
file_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
'../%s' % filename))
return file_path
def deprecate_warning(what, as_of, in_favor_of=None, remove_in=1):
versionutils.deprecation_warning(as_of=as_of, what=what,
in_favor_of=in_favor_of,
remove_in=remove_in)

View File

@@ -40,7 +40,7 @@ _mixed_case_fields = ('vnfInstanceName', 'vnfInstanceDescription', 'vnfdId',
'vnfProvider', 'vnfProductName', 'vnfSoftwareVersion',
'vnfdVersion', 'instantiationState',
'vimConnectionInfo', 'instantiatedVnfInfo',
'vnfConfigurableProperties', 'vnfPkgId')
'vnfConfigurableProperties')
_VNF_INSTANCE = 'vnf_instance'
@@ -67,7 +67,7 @@ def _get_columns(vnflcm_obj, action="/?originalUrl=https%3A%2F%2Fgit.openstack.org%2FNone)%3A%253C%2Fcode">
'vnfdVersion': 'VNFD Version',
'instantiationState': 'Instantiation State',
'_links': 'Links',
'vnfConfigurableProperties': 'VNF Configurable Properties'
'vnfConfigurableProperties': 'VNF Configurable Properties',
}
if action == 'show':
if vnflcm_obj['instantiationState'] == 'INSTANTIATED':
@@ -78,12 +78,6 @@ def _get_columns(vnflcm_obj, action="/?originalUrl=https%3A%2F%2Fgit.openstack.org%2FNone)%3A%253C%2Fcode">
{'vimConnectionInfo': 'VIM Connection Info',
'_links': 'Links'}
)
# Note: To prevent it from appearing in the v2 API output,
# the 'VNF Package ID' will be output only if the vnfPkgId exists.
if 'vnfPkgId' in vnflcm_obj:
column_map.update(
{'vnfPkgId': 'VNF Package ID'}
)
return sdk_utils.get_osc_show_columns_for_sdk_resource(vnflcm_obj,
column_map)

View File

@@ -24,14 +24,12 @@ from unittest import mock
import ddt
import zipfile
from tackerclient import client as root_client
from tackerclient.common import exceptions
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.osc.v1.vnfpkgm import vnf_package
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1.fixture_data import client
from tackerclient.tests.unit.osc.v1 import vnf_package_fakes
from tackerclient.tests.unit.test_cli10 import MyResp
from tackerclient.v1_0 import client as proxy_client
@@ -517,32 +515,6 @@ class TestUploadVnfPackage(TestVnfPackage):
# check no fault response is received
self.assertNotCalled(m)
@ddt.data('path')
@mock.patch.object(proxy_client.ClientBase, 'deserialize')
def test_upload_vnf_package_check_content_type(self, method, mock_des):
path = None
if method == 'path':
zip_file, temp_dir = _create_zip()
path = zip_file
arglist, verifylist = self._get_arglist_and_verifylist(method, path)
parsed_args = self.check_parser(self.upload_vnf_package, arglist,
verifylist)
mock_des.return_value = {}
with mock.patch.object(root_client.HTTPClient,
'do_request') as mock_req:
headers = {'Content-Type': 'application/json'}
mock_req.return_value = (MyResp(202, headers=headers), None)
self._mock_request_url_for_upload('PUT')
self.upload_vnf_package.take_action(parsed_args)
# Delete temporary folder
shutil.rmtree(temp_dir)
mock_req.assert_called_once_with(
f'/vnfpkgm/v1/vnf_packages/{self._vnf_package["id"]}'
'/package_content', 'PUT',
body=mock.ANY, headers=mock.ANY,
content_type='application/zip', accept='json')
def test_upload_vnf_package_with_conflict_error(self):
# Scenario in which vnf package is already in on-boarded state
zip_file, temp_dir = _create_zip()
@@ -633,37 +605,6 @@ class TestUpdateVnfPackage(TestVnfPackage):
self.assertListItemsEqual(
vnf_package_fakes.get_vnf_package_data(fake_response), data)
@ddt.data((["--user-data", 'Test_key=Test_value'],
[('user_data', {'Test_key': 'Test_value'})]))
@ddt.unpack
@mock.patch.object(proxy_client.ClientBase, 'deserialize')
def test_take_action_check_content_type(
self, arglist, verifylist, mock_des):
vnf_package_obj = vnf_package_fakes.vnf_package_obj(
onboarded_state=True)
arglist.append(vnf_package_obj['id'])
verifylist.append(('vnf_package', vnf_package_obj['id']))
mock_des.return_value = {}
parsed_args = self.check_parser(self.update_vnf_package, arglist,
verifylist)
url = os.path.join(self.url, 'vnfpkgm/v1/vnf_packages',
vnf_package_obj['id'])
fake_response = vnf_package_fakes.get_fake_update_vnf_package_obj(
arglist)
self.requests_mock.register_uri('PATCH', url, json=fake_response,
headers=self.header)
with mock.patch.object(root_client.HTTPClient,
'do_request') as mock_req:
headers = {'Content-Type': 'application/json'}
mock_req.return_value = (MyResp(200, headers=headers), None)
self.update_vnf_package.take_action(parsed_args)
mock_req.assert_called_once_with(
f'/vnfpkgm/v1/vnf_packages/{vnf_package_obj["id"]}', 'PATCH',
body=mock.ANY, headers=mock.ANY,
content_type='application/merge-patch+json', accept='json')
def test_update_no_options(self):
self.assertRaises(base.ParserException, self.check_parser,
self.update_vnf_package, [], [])

View File

@@ -22,14 +22,12 @@ from unittest import mock
import ddt
from oslo_utils.fixture import uuidsentinel
from tackerclient import client as root_client
from tackerclient.common import exceptions
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.osc.v1.vnflcm import vnflcm
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1.fixture_data import client
from tackerclient.tests.unit.osc.v1 import vnflcm_fakes
from tackerclient.tests.unit.test_cli10 import MyResp
from tackerclient.v1_0 import client as proxy_client
@@ -50,13 +48,13 @@ def _get_columns_vnflcm(action="/?originalUrl=https%3A%2F%2Fgit.openstack.org%2F%26%2339%3Bcreate%26%2339%3B)%3A%253C%2Fcode">
columns = ['ID', 'Instantiation State', 'VNF Instance Description',
'VNF Instance Name', 'VNF Product Name', 'VNF Provider',
'VNF Software Version', 'VNFD ID', 'VNFD Version', 'Links',
'VNF Configurable Properties', 'VNF Package ID']
'VNF Configurable Properties']
if action == 'show':
columns.extend(['Instantiated Vnf Info', 'VIM Connection Info'])
if action == 'list':
columns = [ele for ele in columns if ele not in
['VNFD Version', 'VNF Instance Description',
'VNF Configurable Properties', 'VNF Package ID']]
'VNF Configurable Properties']]
columns.remove('Links')
return columns
@@ -661,45 +659,6 @@ class TestUpdateVnfLcm(TestVnfLcm):
self.assertEqual(expected_message, actual_message)
@mock.patch.object(proxy_client.ClientBase, 'deserialize')
def test_take_action_check_content_type(self, mock_des):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = ('./tackerclient/osc/v1/vnflcm/samples/'
'update_vnf_instance_param_sample.json')
arglist = [vnf_instance['id'], '--I', sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('I', sample_param_file)]
mock_des.return_value = {}
# command param
parsed_args = self.check_parser(
self.update_vnf_lcm, arglist, verifylist)
url = os.path.join(self.url, 'vnflcm/v1/vnf_instances',
vnf_instance['id'])
self.requests_mock.register_uri(
'PATCH', url, headers=self.header, json={})
sys.stdout = buffer = StringIO()
with mock.patch.object(root_client.HTTPClient,
'do_request') as mock_req:
headers = {'Content-Type': 'application/json'}
mock_req.return_value = (MyResp(202, headers=headers), None)
self.update_vnf_lcm.take_action(parsed_args)
# check content_type
mock_req.assert_called_once_with(
f'/vnflcm/v1/vnf_instances/{vnf_instance["id"]}', 'PATCH',
body=mock.ANY, headers=mock.ANY,
content_type='application/merge-patch+json', accept='json')
actual_message = buffer.getvalue().strip()
expected_message = f'Update vnf:{vnf_instance["id"]}'
self.assertEqual(expected_message, actual_message)
def test_take_action_param_file_not_exists(self):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = "./not_exists.json"

View File

@@ -39,7 +39,6 @@ def vnf_instance_response(attrs=None, instantiation_state='NOT_INSTANTIATED'):
"vnfProductName": "Sample VNF",
"vnfSoftwareVersion": "1.0",
"vnfdVersion": "1.0",
"vnfPkgId": uuidsentinel.uuid,
"_links": "vnflcm/v1/vnf_instances/" + uuidsentinel.vnf_instance_id +
"/instantiate",
"instantiationState": instantiation_state,

View File

@@ -19,15 +19,12 @@ import os
from oslo_utils.fixture import uuidsentinel
from unittest import mock
from tackerclient import client as root_client
from tackerclient.common import exceptions
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.osc.v2.vnffm import vnffm_alarm
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1.fixture_data import client
from tackerclient.tests.unit.osc.v2 import vnffm_alarm_fakes
from tackerclient.tests.unit.test_cli10 import MyResp
from tackerclient.v1_0 import client as proxy_client
class TestVnfFmAlarm(base.FixturedTestCase):
@@ -265,39 +262,6 @@ class TestUpdateVnfFmAlarm(TestVnfFmAlarm):
self.assertEqual(expected_data, data)
@ddt.data('ACKNOWLEDGED')
@mock.patch.object(proxy_client.ClientBase, 'deserialize')
def test_take_action_check_content_type(self, ack_state, mock_des):
"""Check content type by test of take_action()"""
vnffm_alarm_obj = vnffm_alarm_fakes.vnf_fm_alarm_response(
None, 'update')
arg_list = ['--ack-state', ack_state, uuidsentinel.vnf_fm_alarm_id]
verify_list = [('ack_state', ack_state),
('vnf_fm_alarm_id', uuidsentinel.vnf_fm_alarm_id)]
mock_des.return_value = {}
# command param
parsed_args = self.check_parser(
self.update_vnf_fm_alarm, arg_list, verify_list)
url = os.path.join(
self.url, 'vnffm/v1/alarms', uuidsentinel.vnf_fm_alarm_id)
self.requests_mock.register_uri(
'PATCH', url, headers=self.header, json=vnffm_alarm_obj)
with mock.patch.object(root_client.HTTPClient,
'do_request') as mock_req:
headers = {'Content-Type': 'application/json'}
mock_req.return_value = (MyResp(200, headers=headers), None)
self.update_vnf_fm_alarm.take_action(parsed_args)
mock_req.assert_called_once_with(
f'/vnffm/v1/alarms/{uuidsentinel.vnf_fm_alarm_id}',
'PATCH', body=mock.ANY, headers=mock.ANY,
content_type='application/merge-patch+json', accept='json')
@ddt.data('ACKNOWLEDGED')
def test_take_action_vnf_lcm_op_occ_id_not_found(self, ack_state):
"""Test if vnf-lcm-op-occ-id does not find"""

View File

@@ -18,14 +18,12 @@ import os
import sys
from unittest import mock
from tackerclient import client as root_client
from tackerclient.common import exceptions
from tackerclient.osc.v1.vnflcm import vnflcm
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1.fixture_data import client
from tackerclient.tests.unit.osc.v1 import test_vnflcm
from tackerclient.tests.unit.osc.v1 import vnflcm_fakes
from tackerclient.tests.unit.test_cli10 import MyResp
from tackerclient.v1_0 import client as proxy_client
@@ -44,50 +42,6 @@ class TestVnfLcmV2(base.FixturedTestCase):
# check of other paths is omitted.
class TestUpdateVnfLcmV2(test_vnflcm.TestVnfLcm):
api_version = '2'
def setUp(self):
super(TestUpdateVnfLcmV2, self).setUp()
self.update_vnf_lcm = vnflcm.UpdateVnfLcm(
self.app, self.app_args, cmd_name='vnflcm modify')
def test_take_action_check_content_type(self):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = ('./tackerclient/osc/v1/vnflcm/samples/'
'update_vnf_instance_param_sample.json')
arglist = [vnf_instance['id'], '--I', sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('I', sample_param_file)]
# command param
parsed_args = self.check_parser(
self.update_vnf_lcm, arglist, verifylist)
url = os.path.join(self.url, 'vnflcm/v2/vnf_instances',
vnf_instance['id'])
self.requests_mock.register_uri(
'PATCH', url, headers=self.header, json={})
sys.stdout = buffer = StringIO()
with mock.patch.object(root_client.HTTPClient,
'do_request') as mock_req:
headers = {'Content-Type': 'application/json'}
mock_req.return_value = (MyResp(202, headers=headers), None)
self.update_vnf_lcm.take_action(parsed_args)
# check content_type
mock_req.assert_called_once_with(
f'/vnflcm/v2/vnf_instances/{vnf_instance["id"]}', 'PATCH',
body=mock.ANY, headers=mock.ANY,
content_type='application/merge-patch+json', accept='json')
actual_message = buffer.getvalue().strip()
expected_message = f'Update vnf:{vnf_instance["id"]}'
self.assertEqual(expected_message, actual_message)
class TestChangeVnfPkgVnfLcm(test_vnflcm.TestVnfLcm):
api_version = '2'

View File

@@ -21,15 +21,12 @@ from io import StringIO
from oslo_utils.fixture import uuidsentinel
from unittest import mock
from tackerclient import client as root_client
from tackerclient.common import exceptions
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.osc.v2.vnfpm import vnfpm_job
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1.fixture_data import client
from tackerclient.tests.unit.osc.v2 import vnfpm_job_fakes
from tackerclient.tests.unit.test_cli10 import MyResp
from tackerclient.v1_0 import client as proxy_client
class TestVnfPmJob(base.FixturedTestCase):
@@ -381,39 +378,6 @@ class TestUpdateVnfPmJob(TestVnfPmJob):
vnfpm_job_obj, columns=columns)
self.assertEqual(expected_data, data)
@mock.patch.object(proxy_client.ClientBase, 'deserialize')
def test_take_action_check_content_type(self, mock_des):
"""Check content type by test of take_action()"""
param_file = ('./tackerclient/osc/v2/vnfpm/samples/'
'update_vnf_pm_job_param_sample.json')
arg_list = [uuidsentinel.vnf_pm_job_id, param_file]
verify_list = [
('vnf_pm_job_id', uuidsentinel.vnf_pm_job_id),
('request_file', param_file)
]
vnfpm_job_obj = vnfpm_job_fakes.vnf_pm_job_response(
None, 'update')
mock_des.return_value = {}
# command param
parsed_args = self.check_parser(
self.update_vnf_pm_job, arg_list, verify_list)
url = os.path.join(
self.url, 'vnfpm/v2/pm_jobs', uuidsentinel.vnf_pm_job_id)
self.requests_mock.register_uri(
'PATCH', url, headers=self.header, json=vnfpm_job_obj)
with mock.patch.object(root_client.HTTPClient,
'do_request') as mock_req:
headers = {'Content-Type': 'application/json'}
mock_req.return_value = (MyResp(200, headers=headers), None)
self.update_vnf_pm_job.take_action(parsed_args)
mock_req.assert_called_once_with(
f'/vnfpm/v2/pm_jobs/{uuidsentinel.vnf_pm_job_id}', 'PATCH',
body=mock.ANY, headers=mock.ANY,
content_type='application/merge-patch+json', accept='json')
def test_take_action_vnf_pm_job_id_not_found(self):
"""Test if vnf-pm-job-id does not find"""

View File

@@ -21,15 +21,12 @@ from io import StringIO
from oslo_utils.fixture import uuidsentinel
from unittest import mock
from tackerclient import client as root_client
from tackerclient.common import exceptions
from tackerclient.osc import utils as tacker_osc_utils
from tackerclient.osc.v2.vnfpm import vnfpm_threshold
from tackerclient.tests.unit.osc import base
from tackerclient.tests.unit.osc.v1.fixture_data import client
from tackerclient.tests.unit.osc.v2 import vnfpm_threshold_fakes
from tackerclient.tests.unit.test_cli10 import MyResp
from tackerclient.v1_0 import client as proxy_client
class TestVnfPmThreshold(base.FixturedTestCase):
@@ -303,38 +300,6 @@ class TestUpdateVnfPmThreshold(TestVnfPmThreshold):
vnfpm_threshold_obj, columns=columns)
self.assertEqual(expected_data, data)
@mock.patch.object(proxy_client.ClientBase, 'deserialize')
def test_take_action_check_content_type(self, mock_des):
param_file = ('./tackerclient/osc/v2/vnfpm/samples/'
'update_vnf_pm_threshold_param_sample.json')
arg_list = [uuidsentinel.vnf_pm_threshold_id, param_file]
verify_list = [
('vnf_pm_threshold_id', uuidsentinel.vnf_pm_threshold_id),
('request_file', param_file)
]
vnfpm_threshold_obj = vnfpm_threshold_fakes.vnf_pm_threshold_response(
None, 'update')
mock_des.return_value = {}
# command param
parsed_args = self.check_parser(
self.update_vnf_pm_threshold, arg_list, verify_list)
url = os.path.join(
self.url, 'vnfpm/v2/thresholds',
uuidsentinel.vnf_pm_threshold_id)
self.requests_mock.register_uri(
'PATCH', url, headers=self.header, json=vnfpm_threshold_obj)
with mock.patch.object(root_client.HTTPClient,
'do_request') as mock_req:
headers = {'Content-Type': 'application/json'}
mock_req.return_value = (MyResp(200, headers=headers), None)
self.update_vnf_pm_threshold.take_action(parsed_args)
mock_req.assert_called_once_with(
f'/vnfpm/v2/thresholds/{uuidsentinel.vnf_pm_threshold_id}',
'PATCH', body=mock.ANY, headers=mock.ANY,
content_type='application/merge-patch+json', accept='json')
def test_take_action_vnf_pm_threshold_id_not_found(self):
param_file = ("./tackerclient/osc/v2/vnfpm/samples/"
"update_vnf_pm_threshold_param_sample.json")

View File

@@ -182,7 +182,6 @@ class ClientBase(object):
self.retry_interval = 1
self.rel = None
self.params = None
self.accept = None
def _handle_fault_response(self, status_code, response_body):
# Create exception with HTTP status code and message
@@ -238,7 +237,7 @@ class ClientBase(object):
resp, replybody = self.httpclient.do_request(
action, method, body=body, headers=headers,
content_type=self.content_type(), accept=self.accept)
content_type=self.content_type())
if 'application/zip' == resp.headers.get('Content-Type'):
self.format = 'zip'
@@ -358,8 +357,6 @@ class ClientBase(object):
headers=headers, params=params)
def patch(self, action, body=None, headers=None, params=None):
self.format = 'merge-patch+json'
self.accept = 'json'
return self.retry_request("PATCH", action, body=body,
headers=headers, params=params)
@@ -483,7 +480,6 @@ class VnfPackageClient(ClientBase):
body=json)
else:
self.format = 'zip'
self.accept = 'json'
return self.put('{base_path}/{id}/package_content'.format(
id=vnf_package,
base_path=self.vnfpackages_path),
@@ -815,7 +811,7 @@ class VnfPMClient(ClientBase):
class Client(object):
"""Unified interface to interact with multiple apps of tacker service.
"""Unified interface to interact with multiple applications of tacker service.
This class is a single entry point to interact with legacy tacker apis and
vnf packages apis.

View File

@@ -2,7 +2,7 @@
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
hacking>=7.0.0,<7.1.0 # Apache-2.0
hacking>=4.0.0,<4.1.0 # Apache-2.0
coverage!=4.4,>=4.0 # Apache-2.0
ddt>=1.0.1 # MIT
fixtures>=3.0.0 # Apache-2.0/BSD

View File

@@ -11,7 +11,7 @@ setenv = VIRTUAL_ENV={envdir}
LC_ALL=C
usedevelop = True
deps =
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2024.2}
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
commands = stestr run --slowest {posargs}
@@ -29,7 +29,7 @@ commands = sphinx-build -W -b html doc/source doc/build/html
[testenv:releasenotes]
deps =
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/2024.2}
-r{toxinidir}/doc/requirements.txt
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html