Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-glanceclient for
openSUSE:Factory checked in at 2026-09-08 16:55:33
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-glanceclient (Old)
and /work/SRC/openSUSE:Factory/.python-glanceclient.new.1265 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-glanceclient"
Tue Sep 8 16:55:33 2026 rev:46 rq:1376145 version:4.13.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-glanceclient/python-glanceclient.changes
2026-08-09 21:37:35.974859754 +0200
+++
/work/SRC/openSUSE:Factory/.python-glanceclient.new.1265/python-glanceclient.changes
2026-09-08 16:57:41.830938880 +0200
@@ -1,0 +2,14 @@
+Mon Sep 7 09:42:52 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 4.13.0:
+ * Remove Python 3.10 support
+ * Add pyproject.toml to support pip 23.1
+ * Fix outdated default envlist
+ * Replace deprecated datetime.utcfromtimestamp
+ * Drop fallback to UPPER_CONSTRAINTS_FILE
+ * Apply upper constraints to build documentation
+ * Add support for cache clean and prune APIs
+ * Add support for GET /v2/cache/nodes/{image_id}
+ * Zuul: do not use USE_PYTHON3
+
+-------------------------------------------------------------------
Old:
----
python_glanceclient-4.12.0.tar.gz
New:
----
python_glanceclient-4.13.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-glanceclient.spec ++++++
--- /var/tmp/diff_new_pack.XmdePO/_old 2026-09-08 16:57:43.525009481 +0200
+++ /var/tmp/diff_new_pack.XmdePO/_new 2026-09-08 16:57:43.527009564 +0200
@@ -18,7 +18,7 @@
%global pythons %{primary_python}
Name: python-glanceclient
-Version: 4.12.0
+Version: 4.13.0
Release: 0
Summary: Python API and CLI for OpenStack Glance
License: Apache-2.0
++++++ python_glanceclient-4.12.0.tar.gz -> python_glanceclient-4.13.0.tar.gz
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_glanceclient-4.12.0/.zuul.yaml
new/python_glanceclient-4.13.0/.zuul.yaml
--- old/python_glanceclient-4.12.0/.zuul.yaml 2026-05-19 11:25:56.000000000
+0200
+++ new/python_glanceclient-4.13.0/.zuul.yaml 2026-08-27 12:28:29.282551500
+0200
@@ -59,13 +59,6 @@
vars:
tox_envlist: py3
-- job:
- name: glanceclient-dsvm-functional-py3
- parent: glanceclient-dsvm-functional
- vars:
- devstack_localrc:
- USE_PYTHON3: true
-
- project:
templates:
- check-requirements
@@ -98,6 +91,3 @@
branches: master
- glanceclient-tox-py3-oslo-tips:
branches: master
- experimental:
- jobs:
- - glanceclient-dsvm-functional-py3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_glanceclient-4.12.0/ChangeLog
new/python_glanceclient-4.13.0/ChangeLog
--- old/python_glanceclient-4.12.0/ChangeLog 2026-05-19 11:26:32.000000000
+0200
+++ new/python_glanceclient-4.13.0/ChangeLog 2026-08-27 12:29:02.659659000
+0200
@@ -1,6 +1,19 @@
CHANGES
=======
+4.13.0
+------
+
+* Remove Python 3.10 support
+* Add pyproject.toml to support pip 23.1
+* Fix outdated default envlist
+* Replace deprecated datetime.utcfromtimestamp
+* Drop fallback to UPPER\_CONSTRAINTS\_FILE
+* Apply upper constraints to build documentation
+* Add support for cache clean and prune APIs
+* Add support for GET /v2/cache/nodes/{image\_id}
+* Zuul: do not use USE\_PYTHON3
+
4.12.0
------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_glanceclient-4.12.0/PKG-INFO
new/python_glanceclient-4.13.0/PKG-INFO
--- old/python_glanceclient-4.12.0/PKG-INFO 2026-05-19 11:26:32.340457000
+0200
+++ new/python_glanceclient-4.13.0/PKG-INFO 2026-08-27 12:29:02.760661000
+0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: python-glanceclient
-Version: 4.12.0
+Version: 4.13.0
Summary: OpenStack Image API Client Library
Home-page: https://docs.openstack.org/python-glanceclient/latest/
Author: OpenStack
@@ -17,10 +17,10 @@
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
-Requires-Python: >=3.10
+Classifier: Programming Language :: Python :: 3.13
+Requires-Python: >=3.11
License-File: LICENSE
Requires-Dist: pbr!=2.1.0,>=2.0.0
Requires-Dist: PrettyTable>=0.7.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_glanceclient-4.12.0/glanceclient/common/utils.py
new/python_glanceclient-4.13.0/glanceclient/common/utils.py
--- old/python_glanceclient-4.12.0/glanceclient/common/utils.py 2026-05-19
11:25:56.000000000 +0200
+++ new/python_glanceclient-4.13.0/glanceclient/common/utils.py 2026-08-27
12:28:29.284551600 +0200
@@ -208,15 +208,18 @@
if last_accessed == 0:
last_accessed = "N/A"
else:
- last_accessed = datetime.datetime.utcfromtimestamp(
- last_accessed).isoformat()
+ last_accessed = datetime.datetime.fromtimestamp(
+ last_accessed, tz=datetime.timezone.utc
+ ).replace(tzinfo=None).isoformat()
- cache_pt.add_row((image['image_id'], state,
- last_accessed,
- datetime.datetime.utcfromtimestamp(
- image['last_modified']).isoformat(),
- image['size'],
- image['hits']))
+ cache_pt.add_row((
+ image['image_id'], state,
+ last_accessed,
+ datetime.datetime.fromtimestamp(
+ image['last_modified'], tz=datetime.timezone.utc
+ ).replace(tzinfo=None).isoformat(),
+ image['size'],
+ image['hits']))
else:
for image in cached_images[item]:
cache_pt.add_row((image,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_glanceclient-4.12.0/glanceclient/tests/unit/v2/test_cache.py
new/python_glanceclient-4.13.0/glanceclient/tests/unit/v2/test_cache.py
--- old/python_glanceclient-4.12.0/glanceclient/tests/unit/v2/test_cache.py
2026-05-19 11:25:56.000000000 +0200
+++ new/python_glanceclient-4.13.0/glanceclient/tests/unit/v2/test_cache.py
2026-08-27 12:28:29.287551600 +0200
@@ -64,6 +64,27 @@
'',
),
},
+ '/v2/cache/nodes/3a4560a1-e585-443e-9b39-553b46ec92d1': {
+ 'GET': (
+ {},
+ ['http://node1.example', 'http://node2.example'],
+ ),
+ },
+ '/v2/cache/clean': {
+ 'POST': (
+ {},
+ '',
+ ),
+ },
+ '/v2/cache/prune': {
+ 'POST': (
+ {},
+ {
+ 'total_files_pruned': 5,
+ 'total_bytes_pruned': 104857600,
+ },
+ ),
+ },
}
@@ -133,3 +154,68 @@
mock_has_version.return_value = False
self.assertRaises(exc.HTTPNotImplemented,
self.controller.list)
+
+ @mock.patch.object(common_utils, 'has_version')
+ def test_list_cached_nodes(self, mock_has_version):
+ mock_has_version.return_value = True
+ image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1'
+ nodes = self.controller.list_cached_nodes(image_id)
+ self.assertEqual(['http://node1.example', 'http://node2.example'],
+ list(nodes))
+ expect = [('GET', '/v2/cache/nodes/%s' % image_id, {}, None)]
+ self.assertEqual(expect, self.api.calls)
+
+ @mock.patch.object(common_utils, 'has_version')
+ def test_list_cached_nodes_empty(self, mock_has_version):
+ mock_has_version.return_value = True
+ image_id = 'df601a47-7251-4d20-84ae-07de335af424'
+ dummy_fixtures = {
+ '/v2/cache/nodes/%s' % image_id: {
+ 'GET': (
+ {},
+ [],
+ ),
+ }
+ }
+ dummy_api = utils.FakeAPI(dummy_fixtures)
+ dummy_controller = cache.Controller(dummy_api)
+ nodes = dummy_controller.list_cached_nodes(image_id)
+ self.assertEqual([], list(nodes))
+ self.assertEqual(
+ [('GET', '/v2/cache/nodes/%s' % image_id, {}, None)],
+ dummy_api.calls)
+
+ @mock.patch.object(common_utils, 'has_version')
+ def test_list_cached_nodes_not_supported(self, mock_has_version):
+ mock_has_version.return_value = False
+ self.assertRaises(exc.HTTPNotImplemented,
+ self.controller.list_cached_nodes,
+ '3a4560a1-e585-443e-9b39-553b46ec92d1')
+
+ @mock.patch.object(common_utils, 'has_version')
+ def test_cache_clean(self, mock_has_version):
+ mock_has_version.return_value = True
+ self.controller.clean()
+ expect = [('POST', '/v2/cache/clean', {}, None)]
+ self.assertEqual(expect, self.api.calls)
+
+ @mock.patch.object(common_utils, 'has_version')
+ def test_cache_prune(self, mock_has_version):
+ mock_has_version.return_value = True
+ result = self.controller.prune()
+ expect = [('POST', '/v2/cache/prune', {}, None)]
+ self.assertEqual(expect, self.api.calls)
+ self.assertEqual(5, result['total_files_pruned'])
+ self.assertEqual(104857600, result['total_bytes_pruned'])
+
+ @mock.patch.object(common_utils, 'has_version')
+ def test_cache_clean_not_supported(self, mock_has_version):
+ mock_has_version.return_value = False
+ self.assertRaises(exc.HTTPNotImplemented,
+ self.controller.clean)
+
+ @mock.patch.object(common_utils, 'has_version')
+ def test_cache_prune_not_supported(self, mock_has_version):
+ mock_has_version.return_value = False
+ self.assertRaises(exc.HTTPNotImplemented,
+ self.controller.prune)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_glanceclient-4.12.0/glanceclient/tests/unit/v2/test_shell_v2.py
new/python_glanceclient-4.13.0/glanceclient/tests/unit/v2/test_shell_v2.py
--- old/python_glanceclient-4.12.0/glanceclient/tests/unit/v2/test_shell_v2.py
2026-05-19 11:25:56.000000000 +0200
+++ new/python_glanceclient-4.13.0/glanceclient/tests/unit/v2/test_shell_v2.py
2026-08-27 12:28:29.311551600 +0200
@@ -3799,6 +3799,106 @@
'Direct server endpoint needs to be provided. Do '
'not use loadbalanced or catalog endpoints.')
+ def test_do_cache_nodes_list(self):
+ args = argparse.Namespace(
+ id='3a4560a1-e585-443e-9b39-553b46ec92d1')
+ expected_nodes = ['http://node1.example', 'http://node2.example']
+ with mock.patch.object(
+ self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
+ mocked_list_nodes.return_value = expected_nodes
+ test_shell.do_cache_nodes_list(self.gc, args)
+ mocked_list_nodes.assert_called_once_with(args.id)
+ objs, fields = utils.print_list.call_args[0]
+ self.assertEqual(['Node Reference URL'], fields)
+ self.assertEqual(2, len(objs))
+ self.assertEqual('http://node1.example', objs[0].node_reference_url)
+ self.assertEqual('http://node2.example', objs[1].node_reference_url)
+
+ def test_do_cache_nodes_list_empty(self):
+ args = argparse.Namespace(
+ id='3a4560a1-e585-443e-9b39-553b46ec92d1')
+ with mock.patch.object(
+ self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
+ mocked_list_nodes.return_value = []
+ test_shell.do_cache_nodes_list(self.gc, args)
+ objs, fields = utils.print_list.call_args[0]
+ self.assertEqual(['Node Reference URL'], fields)
+ self.assertEqual(0, len(objs))
+
+ def test_do_cache_nodes_list_unsupported(self):
+ args = argparse.Namespace(
+ id='3a4560a1-e585-443e-9b39-553b46ec92d1')
+ with mock.patch.object(
+ self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
+ mocked_list_nodes.side_effect = exc.HTTPNotImplemented
+ self.assertRaises(exc.HTTPNotImplemented,
+ test_shell.do_cache_nodes_list,
+ self.gc, args)
+
+ def test_do_cache_nodes_list_forbidden(self):
+ image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1'
+ args = argparse.Namespace(id=image_id)
+ with mock.patch.object(
+ self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
+ mocked_list_nodes.side_effect = exc.HTTPForbidden
+ with mock.patch(
+ 'glanceclient.common.utils.print_err') as mock_print_err:
+ test_shell.do_cache_nodes_list(self.gc, args)
+ mock_print_err.assert_called_once_with(
+ "You are not permitted to list cached nodes for image '%s'."
+ % image_id)
+
+ def test_do_cache_nodes_list_conflict(self):
+ image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1'
+ args = argparse.Namespace(id=image_id)
+ with mock.patch.object(
+ self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
+ mocked_list_nodes.side_effect = exc.HTTPConflict
+ with mock.patch(
+ 'glanceclient.common.utils.print_err') as mock_print_err:
+ test_shell.do_cache_nodes_list(self.gc, args)
+ mock_print_err.assert_called_once_with(
+ "'%s': Unable to list cached nodes for image '%s'."
+ % (exc.HTTPConflict(), image_id))
+
+ def test_do_cache_nodes_list_not_found(self):
+ image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1'
+ args = argparse.Namespace(id=image_id)
+ with mock.patch.object(
+ self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
+ mocked_list_nodes.side_effect = exc.HTTPNotFound
+ with mock.patch(
+ 'glanceclient.common.utils.print_err') as mock_print_err:
+ test_shell.do_cache_nodes_list(self.gc, args)
+ mock_print_err.assert_called_once_with(
+ "'%s': Unable to list cached nodes for image '%s'."
+ % (exc.HTTPNotFound(), image_id))
+
+ def test_do_cache_nodes_list_http_exception(self):
+ image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1'
+ args = argparse.Namespace(id=image_id)
+ with mock.patch.object(
+ self.gc.cache, 'list_cached_nodes') as mocked_list_nodes:
+ mocked_list_nodes.side_effect = exc.HTTPBadRequest
+ with mock.patch(
+ 'glanceclient.common.utils.print_err') as mock_print_err:
+ test_shell.do_cache_nodes_list(self.gc, args)
+ mock_print_err.assert_called_once_with(
+ "'%s': Unable to list cached nodes for image '%s'."
+ % (exc.HTTPBadRequest(), image_id))
+
+ def test_do_cache_nodes_list_endpoint_not_provided(self):
+ args = argparse.Namespace(
+ id='3a4560a1-e585-443e-9b39-553b46ec92d1')
+ self.gc.endpoint_provided = False
+ with mock.patch('glanceclient.common.utils.exit') as mock_exit:
+ mock_exit.side_effect = self._mock_utils_exit
+ with self.assertRaises(SystemExit):
+ test_shell.do_cache_nodes_list(self.gc, args)
+ mock_exit.assert_called_once_with(
+ 'Direct server endpoint needs to be provided. Do '
+ 'not use loadbalanced or catalog endpoints.')
+
def _test_cache_queue(self, supported=True, forbidden=False,):
args = argparse.Namespace(id=['image1'])
with mock.patch.object(self.gc.cache, 'queue') as mocked_cache_queue:
@@ -3931,3 +4031,91 @@
mock_exit.assert_called_once_with(
'Direct server endpoint needs to be provided. Do '
'not use loadbalanced or catalog endpoints.')
+
+ def _test_cache_clean(self, supported=True, forbidden=False):
+ args = self._make_args({})
+ with mock.patch.object(self.gc.cache, 'clean') as mocked_cache_clean:
+ if supported:
+ mocked_cache_clean.return_value = None
+ else:
+ mocked_cache_clean.side_effect = exc.HTTPNotImplemented
+ if forbidden:
+ mocked_cache_clean.side_effect = exc.HTTPForbidden
+
+ test_shell.do_cache_clean(self.gc, args)
+ if supported:
+ mocked_cache_clean.assert_called_once_with()
+
+ def test_do_cache_clean(self):
+ self._test_cache_clean()
+
+ def test_do_cache_clean_unsupported(self):
+ with mock.patch(
+ 'glanceclient.common.utils.print_err') as mock_print_err:
+ self._test_cache_clean(supported=False)
+ mock_print_err.assert_called_once_with(
+ "'HTTP HTTPNotImplemented': Unable to clean the image cache.")
+
+ def test_do_cache_clean_forbidden(self):
+ with mock.patch(
+ 'glanceclient.common.utils.print_err') as mock_print_err:
+ self._test_cache_clean(forbidden=True)
+ mock_print_err.assert_called_once_with(
+ "You are not permitted to clean the image cache.")
+
+ def test_do_cache_clean_endpoint_not_provided(self):
+ args = self._make_args({})
+ self.gc.endpoint_provided = False
+ with mock.patch('glanceclient.common.utils.exit') as mock_exit:
+ test_shell.do_cache_clean(self.gc, args)
+ mock_exit.assert_called_once_with(
+ 'Direct server endpoint needs to be provided. Do '
+ 'not use loadbalanced or catalog endpoints.')
+
+ def _test_cache_prune(self, supported=True, forbidden=False):
+ args = self._make_args({})
+ with mock.patch.object(self.gc.cache, 'prune') as mocked_cache_prune:
+ if supported:
+ mocked_cache_prune.return_value = {
+ 'total_files_pruned': 5,
+ 'total_bytes_pruned': 104857600,
+ }
+ else:
+ mocked_cache_prune.side_effect = exc.HTTPNotImplemented
+ if forbidden:
+ mocked_cache_prune.side_effect = exc.HTTPForbidden
+
+ with mock.patch('builtins.print') as mock_print:
+ test_shell.do_cache_prune(self.gc, args)
+ if supported and not forbidden:
+ mocked_cache_prune.assert_called_once_with()
+ mock_print.assert_called_once_with(
+ 'Pruned 5 file(s), 104857600 byte(s).')
+
+ def test_do_cache_prune(self):
+ self._test_cache_prune()
+
+ def test_do_cache_prune_unsupported(self):
+ with mock.patch(
+ 'glanceclient.common.utils.print_err') as mock_print_err:
+ self._test_cache_prune(supported=False)
+ mock_print_err.assert_called_once_with(
+ "'HTTP HTTPNotImplemented': Unable to prune the image cache.")
+
+ def test_do_cache_prune_forbidden(self):
+ with mock.patch(
+ 'glanceclient.common.utils.print_err') as mock_print_err:
+ self._test_cache_prune(forbidden=True)
+ mock_print_err.assert_called_once_with(
+ "You are not permitted to prune the image cache.")
+
+ def test_do_cache_prune_endpoint_not_provided(self):
+ args = self._make_args({})
+ self.gc.endpoint_provided = False
+ with mock.patch('glanceclient.common.utils.exit') as mock_exit:
+ mock_exit.side_effect = SystemExit
+ self.assertRaises(SystemExit,
+ test_shell.do_cache_prune, self.gc, args)
+ mock_exit.assert_called_once_with(
+ 'Direct server endpoint needs to be provided. Do not use '
+ 'loadbalanced or catalog endpoints.')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_glanceclient-4.12.0/glanceclient/v2/cache.py
new/python_glanceclient-4.13.0/glanceclient/v2/cache.py
--- old/python_glanceclient-4.12.0/glanceclient/v2/cache.py 2026-05-19
11:25:56.000000000 +0200
+++ new/python_glanceclient-4.13.0/glanceclient/v2/cache.py 2026-08-27
12:28:29.291551600 +0200
@@ -28,7 +28,7 @@
return True
else:
raise exc.HTTPNotImplemented(
- 'Glance does not support image caching API (v2.14)')
+ 'Glance does not support image caching API (%s)' % version)
@utils.add_req_id_to_object()
def list(self):
@@ -60,3 +60,24 @@
url = '/v2/cache/%s' % image_id
resp, body = self.http_client.put(url)
return body, resp
+
+ @utils.add_req_id_to_object()
+ def list_cached_nodes(self, image_id):
+ if self.is_supported('v2.14'):
+ url = '/v2/cache/nodes/%s' % image_id
+ resp, body = self.http_client.get(url)
+ return body, resp
+
+ @utils.add_req_id_to_object()
+ def clean(self):
+ if self.is_supported('v2.18'):
+ url = '/v2/cache/clean'
+ resp, body = self.http_client.post(url)
+ return body, resp
+
+ @utils.add_req_id_to_object()
+ def prune(self):
+ if self.is_supported('v2.18'):
+ url = '/v2/cache/prune'
+ resp, body = self.http_client.post(url)
+ return body, resp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_glanceclient-4.12.0/glanceclient/v2/shell.py
new/python_glanceclient-4.13.0/glanceclient/v2/shell.py
--- old/python_glanceclient-4.12.0/glanceclient/v2/shell.py 2026-05-19
11:25:56.000000000 +0200
+++ new/python_glanceclient-4.13.0/glanceclient/v2/shell.py 2026-08-27
12:28:29.292551500 +0200
@@ -16,6 +16,7 @@
import json
import os
import sys
+from types import SimpleNamespace
from oslo_utils import strutils
@@ -1642,6 +1643,41 @@
utils.print_err(msg)
+def do_cache_clean(gc, args):
+ """Clean invalid and stalled cached images."""
+ if not gc.endpoint_provided:
+ utils.exit("Direct server endpoint needs to be provided. Do not use "
+ "loadbalanced or catalog endpoints.")
+ try:
+ gc.cache.clean()
+ except exc.HTTPForbidden:
+ msg = _("You are not permitted to clean the image cache.")
+ utils.print_err(msg)
+ except exc.HTTPException as e:
+ msg = _("'%s': Unable to clean the image cache." % e)
+ utils.print_err(msg)
+
+
+def do_cache_prune(gc, args):
+ """Prune cached images to reduce cache size."""
+ if not gc.endpoint_provided:
+ utils.exit("Direct server endpoint needs to be provided. Do not use "
+ "loadbalanced or catalog endpoints.")
+ try:
+ result = gc.cache.prune()
+ if result:
+ print(_("Pruned %(files)d file(s), %(bytes)d byte(s).") % {
+ 'files': result.get('total_files_pruned', 0),
+ 'bytes': result.get('total_bytes_pruned', 0),
+ })
+ except exc.HTTPForbidden:
+ msg = _("You are not permitted to prune the image cache.")
+ utils.print_err(msg)
+ except exc.HTTPException as e:
+ msg = _("'%s': Unable to prune the image cache." % e)
+ utils.print_err(msg)
+
+
def do_cache_list(gc, args):
"""Get cache state."""
if not gc.endpoint_provided:
@@ -1651,6 +1687,38 @@
utils.print_cached_images(cached_images)
[email protected]('id', metavar='<IMAGE_ID>',
+ help=_('ID of the image.'))
+def do_cache_nodes_list(gc, args):
+ """List node reference URLs where an image is cached."""
+ if not gc.endpoint_provided:
+ utils.exit("Direct server endpoint needs to be provided. Do not use "
+ "loadbalanced or catalog endpoints.")
+ try:
+ nodes = gc.cache.list_cached_nodes(args.id)
+ except exc.HTTPForbidden:
+ msg = _("You are not permitted to list cached nodes for image '%s'.")
+ utils.print_err(msg % args.id)
+ return
+ except exc.HTTPConflict as e:
+ msg = _("'%s': Unable to list cached nodes for image '%s'.")
+ utils.print_err(msg % (e, args.id))
+ return
+ except exc.HTTPNotFound as e:
+ msg = _("'%s': Unable to list cached nodes for image '%s'.")
+ utils.print_err(msg % (e, args.id))
+ return
+ except exc.HTTPNotImplemented:
+ raise
+ except exc.HTTPException as e:
+ msg = _("'%s': Unable to list cached nodes for image '%s'.")
+ utils.print_err(msg % (e, args.id))
+ return
+
+ rows = [SimpleNamespace(node_reference_url=u) for u in nodes]
+ utils.print_list(rows, ['Node Reference URL'])
+
+
@utils.arg('id', metavar='<IMAGE_ID>', nargs='+',
help=_('ID of image(s) to queue for caching.'))
def do_cache_queue(gc, args):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_glanceclient-4.12.0/pyproject.toml
new/python_glanceclient-4.13.0/pyproject.toml
--- old/python_glanceclient-4.12.0/pyproject.toml 1970-01-01
01:00:00.000000000 +0100
+++ new/python_glanceclient-4.13.0/pyproject.toml 2026-08-27
12:28:29.293551400 +0200
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["pbr>=6.1.1"]
+build-backend = "pbr.build"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_glanceclient-4.12.0/python_glanceclient.egg-info/PKG-INFO
new/python_glanceclient-4.13.0/python_glanceclient.egg-info/PKG-INFO
--- old/python_glanceclient-4.12.0/python_glanceclient.egg-info/PKG-INFO
2026-05-19 11:26:32.000000000 +0200
+++ new/python_glanceclient-4.13.0/python_glanceclient.egg-info/PKG-INFO
2026-08-27 12:29:02.707660000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.4
Name: python-glanceclient
-Version: 4.12.0
+Version: 4.13.0
Summary: OpenStack Image API Client Library
Home-page: https://docs.openstack.org/python-glanceclient/latest/
Author: OpenStack
@@ -17,10 +17,10 @@
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
-Requires-Python: >=3.10
+Classifier: Programming Language :: Python :: 3.13
+Requires-Python: >=3.11
License-File: LICENSE
Requires-Dist: pbr!=2.1.0,>=2.0.0
Requires-Dist: PrettyTable>=0.7.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_glanceclient-4.12.0/python_glanceclient.egg-info/SOURCES.txt
new/python_glanceclient-4.13.0/python_glanceclient.egg-info/SOURCES.txt
--- old/python_glanceclient-4.12.0/python_glanceclient.egg-info/SOURCES.txt
2026-05-19 11:26:32.000000000 +0200
+++ new/python_glanceclient-4.13.0/python_glanceclient.egg-info/SOURCES.txt
2026-08-27 12:29:02.734660400 +0200
@@ -8,6 +8,7 @@
HACKING.rst
LICENSE
README.rst
+pyproject.toml
requirements.txt
run_tests.sh
setup.cfg
@@ -126,6 +127,8 @@
releasenotes/notes/add_new_locations_apis_support-1ceb47178d384d58.yaml
releasenotes/notes/boolean-properties-strict-checking-bdd624b5da81e723.yaml
releasenotes/notes/bp-use-keystoneauth-e12f300e58577b13.yaml
+releasenotes/notes/cache-clean-prune-api-support.yaml
+releasenotes/notes/cache-nodes-list-command.yaml
releasenotes/notes/check-for-md5-59db8fd67870b214.yaml
releasenotes/notes/copy-existing-image-619b7e6bc3394446.yaml
releasenotes/notes/del_from_store-2d807c3038283907.yaml
@@ -145,6 +148,7 @@
releasenotes/notes/multihash-filter-ef2a48dc48fae9dc.yaml
releasenotes/notes/multihash-support-f1474590cf3ef5cf.yaml
releasenotes/notes/pike-relnote-2c77b01aa8799f35.yaml
+releasenotes/notes/remove-py310-5ee8bf147676093b.yaml
releasenotes/notes/remove-py38-e14e0516991682eb.yaml
releasenotes/notes/remove-py39-63731125072287a3.yaml
releasenotes/notes/return-request-id-to-caller-47f4c0a684b1d88e.yaml
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_glanceclient-4.12.0/python_glanceclient.egg-info/pbr.json
new/python_glanceclient-4.13.0/python_glanceclient.egg-info/pbr.json
--- old/python_glanceclient-4.12.0/python_glanceclient.egg-info/pbr.json
2026-05-19 11:26:32.000000000 +0200
+++ new/python_glanceclient-4.13.0/python_glanceclient.egg-info/pbr.json
2026-08-27 12:29:02.707660000 +0200
@@ -1 +1 @@
-{"git_version": "0b06bb4", "is_release": true}
\ No newline at end of file
+{"git_version": "4fb657b", "is_release": true}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_glanceclient-4.12.0/releasenotes/notes/cache-clean-prune-api-support.yaml
new/python_glanceclient-4.13.0/releasenotes/notes/cache-clean-prune-api-support.yaml
---
old/python_glanceclient-4.12.0/releasenotes/notes/cache-clean-prune-api-support.yaml
1970-01-01 01:00:00.000000000 +0100
+++
new/python_glanceclient-4.13.0/releasenotes/notes/cache-clean-prune-api-support.yaml
2026-08-27 12:28:29.293551400 +0200
@@ -0,0 +1,14 @@
+---
+features:
+ - |
+ Client support has been added for the Glance Image API v2.18 cache
+ maintenance endpoints. The following commands have been added to the
+ command line interface:
+
+ * ``cache-clean`` - Clean invalid cache entries and stalled incomplete
+ images
+ * ``cache-prune`` - Prune cached images when the cache size exceeds the
+ maximum configured size
+
+ These commands require a direct glance-api server endpoint and are only
+ available when image caching is enabled on the Glance service.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_glanceclient-4.12.0/releasenotes/notes/cache-nodes-list-command.yaml
new/python_glanceclient-4.13.0/releasenotes/notes/cache-nodes-list-command.yaml
---
old/python_glanceclient-4.12.0/releasenotes/notes/cache-nodes-list-command.yaml
1970-01-01 01:00:00.000000000 +0100
+++
new/python_glanceclient-4.13.0/releasenotes/notes/cache-nodes-list-command.yaml
2026-08-27 12:28:29.293551400 +0200
@@ -0,0 +1,7 @@
+---
+features:
+ - |
+ Add support for the Glance image-cache API ``GET
/v2/cache/nodes/{image_id}``
+ (list node reference URLs where an image is cached when centralized caching
+ is enabled). This adds the ``cache-nodes-list`` shell command and the
+ ``list_cached_nodes`` method on the v2 cache controller.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/python_glanceclient-4.12.0/releasenotes/notes/remove-py310-5ee8bf147676093b.yaml
new/python_glanceclient-4.13.0/releasenotes/notes/remove-py310-5ee8bf147676093b.yaml
---
old/python_glanceclient-4.12.0/releasenotes/notes/remove-py310-5ee8bf147676093b.yaml
1970-01-01 01:00:00.000000000 +0100
+++
new/python_glanceclient-4.13.0/releasenotes/notes/remove-py310-5ee8bf147676093b.yaml
2026-08-27 12:28:29.294551600 +0200
@@ -0,0 +1,5 @@
+---
+upgrade:
+ - |
+ Support for Python 3.10 has been removed. Now the minimum python version
+ supported is 3.11.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_glanceclient-4.12.0/setup.cfg
new/python_glanceclient-4.13.0/setup.cfg
--- old/python_glanceclient-4.12.0/setup.cfg 2026-05-19 11:26:32.340457000
+0200
+++ new/python_glanceclient-4.13.0/setup.cfg 2026-08-27 12:29:02.761661000
+0200
@@ -7,7 +7,7 @@
author = OpenStack
author_email = [email protected]
home_page = https://docs.openstack.org/python-glanceclient/latest/
-python_requires = >=3.10
+python_requires = >=3.11
classifier =
Development Status :: 5 - Production/Stable
Environment :: Console
@@ -20,9 +20,9 @@
Programming Language :: Python :: Implementation :: CPython
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3
- Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
+ Programming Language :: Python :: 3.13
[files]
packages =
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/python_glanceclient-4.12.0/tox.ini
new/python_glanceclient-4.13.0/tox.ini
--- old/python_glanceclient-4.12.0/tox.ini 2026-05-19 11:25:56.000000000
+0200
+++ new/python_glanceclient-4.13.0/tox.ini 2026-08-27 12:28:29.298551600
+0200
@@ -1,5 +1,5 @@
[tox]
-envlist = py39,pep8
+envlist = py3,pep8
minversion = 3.18.0
[testenv]
@@ -7,15 +7,10 @@
setenv = OS_STDOUT_NOCAPTURE=False
OS_STDERR_NOCAPTURE=False
PYTHONDONTWRITEBYTECODE=1
-
-# Nowadays, TOX_CONSTRAINTS_FILE should be used, but some older scripts might
-# still be using UPPER_CONSTRAINTS_FILE, so we check both variables and use the
-# first one that is defined. If none of them is defined, we fallback to the
-# default value.
deps =
-
-c{env:TOX_CONSTRAINTS_FILE:{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}}
- -r{toxinidir}/requirements.txt
- -r{toxinidir}/test-requirements.txt
+
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
+ -r{toxinidir}/requirements.txt
+ -r{toxinidir}/test-requirements.txt
commands = stestr run --slowest {posargs}
[testenv:pep8]
@@ -54,6 +49,7 @@
[testenv:docs]
basepython = python3
deps =
+
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/requirements.txt
-r{toxinidir}/doc/requirements.txt
commands =
@@ -62,12 +58,8 @@
[testenv:releasenotes]
basepython = python3
-# Nowadays, TOX_CONSTRAINTS_FILE should be used, but some older scripts might
-# still be using UPPER_CONSTRAINTS_FILE, so we check both variables and use the
-# first one that is defined. If none of them is defined, we fallback to the
-# default value.
deps =
-
-c{env:TOX_CONSTRAINTS_FILE:{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}}
+
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
-r{toxinidir}/doc/requirements.txt
commands =
sphinx-build -a -E -W -d releasenotes/build/doctrees -b html
releasenotes/source releasenotes/build/html