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-08-09 21:35:10
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-glanceclient (Old)
 and      /work/SRC/openSUSE:Factory/.python-glanceclient.new.16738 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-glanceclient"

Sun Aug  9 21:35:10 2026 rev:45 rq:1370119 version:4.12.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-glanceclient/python-glanceclient.changes  
2026-05-18 17:50:39.510854271 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-glanceclient.new.16738/python-glanceclient.changes
       2026-08-09 21:37:35.974859754 +0200
@@ -1,0 +2,11 @@
+Thu Aug  6 20:40:24 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 4.12.0:
+  * Remove useless glanceclient/common/https.py
+  * Support for downloading image from preferred stores
+  * Fix unit tests with urllib3 2.x
+  * Update master for stable/2026.1
+- drop 0001-Fix-bytes-related-test-failures-with-urllib-3.patch
+  (upstream)
+
+-------------------------------------------------------------------

Old:
----
  0001-Fix-bytes-related-test-failures-with-urllib-3.patch
  python_glanceclient-4.11.0.tar.gz

New:
----
  python_glanceclient-4.12.0.tar.gz

----------(Old B)----------
  Old:  * Update master for stable/2026.1
- drop 0001-Fix-bytes-related-test-failures-with-urllib-3.patch
  (upstream)
----------(Old E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-glanceclient.spec ++++++
--- /var/tmp/diff_new_pack.FICTEz/_old  2026-08-09 21:37:36.494877461 +0200
+++ /var/tmp/diff_new_pack.FICTEz/_new  2026-08-09 21:37:36.498877598 +0200
@@ -18,14 +18,13 @@
 
 %global pythons %{primary_python}
 Name:           python-glanceclient
-Version:        4.11.0
+Version:        4.12.0
 Release:        0
 Summary:        Python API and CLI for OpenStack Glance
 License:        Apache-2.0
 Group:          Development/Languages/Python
 URL:            https://docs.openstack.org/python-glanceclient
 Source0:        
https://files.pythonhosted.org/packages/source/p/python_glanceclient/python_glanceclient-%{version}.tar.gz
-Patch:          0001-Fix-bytes-related-test-failures-with-urllib-3.patch
 BuildRequires:  %{python_module PrettyTable >= 0.7.1}
 BuildRequires:  %{python_module ddt}
 BuildRequires:  %{python_module fixtures}

++++++ python_glanceclient-4.11.0.tar.gz -> python_glanceclient-4.12.0.tar.gz 
++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python_glanceclient-4.11.0/ChangeLog 
new/python_glanceclient-4.12.0/ChangeLog
--- old/python_glanceclient-4.11.0/ChangeLog    2026-02-27 16:44:21.000000000 
+0100
+++ new/python_glanceclient-4.12.0/ChangeLog    2026-05-19 11:26:32.000000000 
+0200
@@ -1,6 +1,14 @@
 CHANGES
 =======
 
+4.12.0
+------
+
+* Remove useless glanceclient/common/https.py
+* Support for downloading image from preferred stores
+* Fix unit tests with urllib3 2.x
+* Update master for stable/2026.1
+
 4.11.0
 ------
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python_glanceclient-4.11.0/PKG-INFO 
new/python_glanceclient-4.12.0/PKG-INFO
--- old/python_glanceclient-4.11.0/PKG-INFO     2026-02-27 16:44:21.827034500 
+0100
+++ new/python_glanceclient-4.12.0/PKG-INFO     2026-05-19 11:26:32.340457000 
+0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: python-glanceclient
-Version: 4.11.0
+Version: 4.12.0
 Summary: OpenStack Image API Client Library
 Home-page: https://docs.openstack.org/python-glanceclient/latest/
 Author: OpenStack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/glanceclient/common/https.py 
new/python_glanceclient-4.12.0/glanceclient/common/https.py
--- old/python_glanceclient-4.11.0/glanceclient/common/https.py 2026-02-27 
16:43:53.000000000 +0100
+++ new/python_glanceclient-4.12.0/glanceclient/common/https.py 1970-01-01 
01:00:00.000000000 +0100
@@ -1,250 +0,0 @@
-# Copyright 2014 Red Hat, Inc
-# All Rights Reserved.
-#
-#    Licensed under the Apache License, Version 2.0 (the "License"); you may
-#    not use this file except in compliance with the License. You may obtain
-#    a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-#    License for the specific language governing permissions and limitations
-#    under the License.
-
-import socket
-import ssl
-import struct
-
-import OpenSSL
-
-
-try:
-    from eventlet import patcher
-    # Handle case where we are running in a monkey patched environment
-    if patcher.is_monkey_patched('socket'):
-        from eventlet.green.httplib import HTTPSConnection
-        from eventlet.green.OpenSSL.SSL import GreenConnection as Connection
-    else:
-        raise ImportError
-except ImportError:
-    import http.client
-    from OpenSSL import SSL
-    HTTPSConnection = http.client.HTTPSConnection
-    Connection = SSL.Connection
-
-
-from glanceclient import exc
-
-
-def verify_callback(host=None):
-    """Provide wrapper for do_verify_callback.
-
-    We use a partial around the 'real' verify_callback function
-    so that we can stash the host value without holding a
-    reference on the VerifiedHTTPSConnection.
-    """
-    def wrapper(connection, x509, errnum,
-                depth, preverify_ok, host=host):
-        return do_verify_callback(connection, x509, errnum,
-                                  depth, preverify_ok, host=host)
-    return wrapper
-
-
-def do_verify_callback(connection, x509, errnum,
-                       depth, preverify_ok, host=None):
-    """Verify the server's SSL certificate.
-
-    This is a standalone function rather than a method to avoid
-    issues around closing sockets if a reference is held on
-    a VerifiedHTTPSConnection by the callback function.
-    """
-    if x509.has_expired():
-        msg = "SSL Certificate expired on '%s'" % x509.get_notAfter()
-        raise exc.SSLCertificateError(msg)
-
-    if depth == 0 and preverify_ok:
-        # We verify that the host matches against the last
-        # certificate in the chain
-        return host_matches_cert(host, x509)
-    else:
-        # Pass through OpenSSL's default result
-        return preverify_ok
-
-
-def host_matches_cert(host, x509):
-    """Verify the certificate identifies the host.
-
-    Verify that the x509 certificate we have received
-    from 'host' correctly identifies the server we are
-    connecting to, ie that the certificate's Common Name
-    or a Subject Alternative Name matches 'host'.
-    """
-    def check_match(name):
-        # Directly match the name
-        if name == host:
-            return True
-
-        # Support single wildcard matching
-        if name.startswith('*.') and host.find('.') > 0:
-            if name[2:] == host.split('.', 1)[1]:
-                return True
-
-    common_name = x509.get_subject().commonName
-
-    # First see if we can match the CN
-    if check_match(common_name):
-        return True
-        # Also try Subject Alternative Names for a match
-    san_list = None
-    for i in range(x509.get_extension_count()):
-        ext = x509.get_extension(i)
-        if ext.get_short_name() == b'subjectAltName':
-            san_list = str(ext)
-            for san in ''.join(san_list.split()).split(','):
-                if san.startswith('DNS:'):
-                    if check_match(san.split(':', 1)[1]):
-                        return True
-
-    # Server certificate does not match host
-    msg = ('Host "%s" does not match x509 certificate contents: '
-           'CommonName "%s"' % (host, common_name))
-    if san_list is not None:
-        msg = msg + ', subjectAltName "%s"' % san_list
-    raise exc.SSLCertificateError(msg)
-
-
-def to_bytes(s):
-    if isinstance(s, str):
-        return bytes(s, 'latin-1')
-    else:
-        return s
-
-
-class OpenSSLConnectionDelegator(object):
-    """An OpenSSL.SSL.Connection delegator.
-
-    Supplies an additional 'makefile' method which httplib requires
-    and is not present in OpenSSL.SSL.Connection.
-
-    Note: Since it is not possible to inherit from OpenSSL.SSL.Connection
-    a delegator must be used.
-    """
-    def __init__(self, *args, **kwargs):
-        self.connection = Connection(*args, **kwargs)
-
-    def __getattr__(self, name):
-        return getattr(self.connection, name)
-
-    def makefile(self, *args, **kwargs):
-        return socket._fileobject(self.connection, *args, **kwargs)
-
-
-class VerifiedHTTPSConnection(HTTPSConnection):
-    """Extended OpenSSL HTTPSConnection for enhanced SSL support.
-
-    Note: Much of this functionality can eventually be replaced
-          with native Python 3.3 code.
-    """
-    # Restrict the set of client supported cipher suites
-    CIPHERS = 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:'\
-              'eCDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:'\
-              'RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS'
-
-    def __init__(self, host, port=None, key_file=None, cert_file=None,
-                 cacert=None, timeout=None, insecure=False,
-                 ssl_compression=True):
-        # List of exceptions reported by Python3 instead of
-        # SSLConfigurationError
-        excp_lst = (TypeError, FileNotFoundError, ssl.SSLError)
-        try:
-            HTTPSConnection.__init__(self, host, port,
-                                     key_file=key_file,
-                                     cert_file=cert_file)
-            self.key_file = key_file
-            self.cert_file = cert_file
-            self.timeout = timeout
-            self.insecure = insecure
-            # NOTE(flaper87): `is_verified` is needed for
-            # requests' urllib3. If insecure is True then
-            # the request is not `verified`, hence `not insecure`
-            self.is_verified = not insecure
-            self.ssl_compression = ssl_compression
-            self.cacert = None if cacert is None else str(cacert)
-            self.set_context()
-            # ssl exceptions are reported in various form in Python 3
-            # so to be compatible, we report the same kind as under
-            # Python2
-        except excp_lst as e:
-            raise exc.SSLConfigurationError(str(e))
-
-    def set_context(self):
-        """Set up the OpenSSL context."""
-        self.context = OpenSSL.SSL.Context(OpenSSL.SSL.SSLv23_METHOD)
-        self.context.set_cipher_list(self.CIPHERS)
-
-        if self.ssl_compression is False:
-            self.context.set_options(0x20000)  # SSL_OP_NO_COMPRESSION
-
-        if self.insecure is not True:
-            self.context.set_verify(OpenSSL.SSL.VERIFY_PEER,
-                                    verify_callback(host=self.host))
-        else:
-            self.context.set_verify(OpenSSL.SSL.VERIFY_NONE,
-                                    lambda *args: True)
-
-        if self.cert_file:
-            try:
-                self.context.use_certificate_file(self.cert_file)
-            except Exception as e:
-                msg = 'Unable to load cert from "%s" %s' % (self.cert_file, e)
-                raise exc.SSLConfigurationError(msg)
-            if self.key_file is None:
-                # We support having key and cert in same file
-                try:
-                    self.context.use_privatekey_file(self.cert_file)
-                except Exception as e:
-                    msg = ('No key file specified and unable to load key '
-                           'from "%s" %s' % (self.cert_file, e))
-                    raise exc.SSLConfigurationError(msg)
-
-        if self.key_file:
-            try:
-                self.context.use_privatekey_file(self.key_file)
-            except Exception as e:
-                msg = 'Unable to load key from "%s" %s' % (self.key_file, e)
-                raise exc.SSLConfigurationError(msg)
-
-        if self.cacert:
-            try:
-                self.context.load_verify_locations(to_bytes(self.cacert))
-            except Exception as e:
-                msg = 'Unable to load CA from "%s" %s' % (self.cacert, e)
-                raise exc.SSLConfigurationError(msg)
-        else:
-            self.context.set_default_verify_paths()
-
-    def connect(self):
-        """Connect to an SSL port using the OpenSSL library.
-
-        This method also applies per-connection parameters to the connection.
-        """
-        result = socket.getaddrinfo(self.host, self.port, 0,
-                                    socket.SOCK_STREAM)
-        if result:
-            socket_family = result[0][0]
-            if socket_family == socket.AF_INET6:
-                sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
-            else:
-                sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        else:
-            # If due to some reason the address lookup fails - we still connect
-            # to IPv4 socket. This retains the older behavior.
-            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        if self.timeout is not None:
-            # '0' microseconds
-            sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO,
-                            struct.pack('LL', self.timeout, 0))
-        self.sock = OpenSSLConnectionDelegator(self.context, sock)
-        self.sock.connect((self.host, self.port))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/glanceclient/tests/unit/test_http.py 
new/python_glanceclient-4.12.0/glanceclient/tests/unit/test_http.py
--- old/python_glanceclient-4.11.0/glanceclient/tests/unit/test_http.py 
2026-02-27 16:43:53.000000000 +0100
+++ new/python_glanceclient-4.12.0/glanceclient/tests/unit/test_http.py 
2026-05-19 11:25:56.000000000 +0200
@@ -318,7 +318,7 @@
 
     def test_http_chunked_request(self):
         text = "Ok"
-        data = io.StringIO(text)
+        data = io.BytesIO(text.encode())
         path = '/v1/images/'
         self.mock.post(self.endpoint + path, text=text)
 
@@ -341,9 +341,9 @@
         self.assertEqual(data, json.loads(self.mock.last_request.body))
 
     def test_http_chunked_response(self):
-        data = "TEST"
+        data = b"TEST"
         path = '/v1/images/'
-        self.mock.get(self.endpoint + path, body=io.StringIO(data),
+        self.mock.get(self.endpoint + path, body=io.BytesIO(data),
                       headers={"Content-Type": "application/octet-stream"})
 
         resp, body = self.client.get(path)
@@ -353,10 +353,10 @@
     @original_only
     def test_log_http_response_with_non_ascii_char(self):
         try:
-            response = 'Ok'
+            response = b'Ok'
             headers = {"Content-Type": "text/plain",
                        "test": "value1\xa5\xa6"}
-            fake = utils.FakeResponse(headers, io.StringIO(response))
+            fake = utils.FakeResponse(headers, io.BytesIO(response))
             self.client.log_http_response(fake)
         except UnicodeDecodeError as e:
             self.fail("Unexpected UnicodeDecodeError exception '%s'" % e)
@@ -457,9 +457,9 @@
 
     def test_log_request_id_once(self):
         logger = self.useFixture(fixtures.FakeLogger(level=logging.DEBUG))
-        data = "TEST"
+        data = b"TEST"
         path = '/v1/images/'
-        self.mock.get(self.endpoint + path, body=io.StringIO(data),
+        self.mock.get(self.endpoint + path, body=io.BytesIO(data),
                       headers={"Content-Type": "application/octet-stream",
                                'x-openstack-request-id': "1234"})
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/glanceclient/tests/unit/test_ssl.py 
new/python_glanceclient-4.12.0/glanceclient/tests/unit/test_ssl.py
--- old/python_glanceclient-4.11.0/glanceclient/tests/unit/test_ssl.py  
2026-02-27 16:43:53.000000000 +0100
+++ new/python_glanceclient-4.12.0/glanceclient/tests/unit/test_ssl.py  
2026-05-19 11:25:56.000000000 +0200
@@ -77,7 +77,7 @@
     def test_v1_requests_cert_verification(self, __):
         """v1 regression test for bug 115260."""
         port = self.port
-        url = 'https://0.0.0.0:%d' % port
+        url = 'https://127.0.0.1:%d' % port
 
         try:
             client = v1.Client(url,
@@ -94,7 +94,7 @@
         """v1 regression test for bug 115260."""
         # Legacy test. Verify 'no compression' has no effect
         port = self.port
-        url = 'https://0.0.0.0:%d' % port
+        url = 'https://127.0.0.1:%d' % port
 
         try:
             client = v1.Client(url,
@@ -110,7 +110,7 @@
     def test_v2_requests_cert_verification(self, __):
         """v2 regression test for bug 115260."""
         port = self.port
-        url = 'https://0.0.0.0:%d' % port
+        url = 'https://127.0.0.1:%d' % port
 
         try:
             gc = v2.Client(url,
@@ -127,7 +127,7 @@
         """v2 regression test for bug 115260."""
         # Legacy test. Verify 'no compression' has no effect
         port = self.port
-        url = 'https://0.0.0.0:%d' % port
+        url = 'https://127.0.0.1:%d' % port
 
         try:
             gc = v2.Client(url,
@@ -143,7 +143,7 @@
     def test_v2_requests_valid_cert_verification(self, __):
         """Test absence of SSL key file."""
         port = self.port
-        url = 'https://0.0.0.0:%d' % port
+        url = 'https://127.0.0.1:%d' % port
         cacert = os.path.join(TEST_VAR_DIR, 'ca.crt')
 
         try:
@@ -158,9 +158,9 @@
 
     @mock.patch('sys.stderr')
     def test_v2_requests_valid_cert_verification_no_compression(self, __):
-        """Test VerifiedHTTPSConnection: absence of SSL key file."""
+        """Test SSL certificate verification with valid CA certificate."""
         port = self.port
-        url = 'https://0.0.0.0:%d' % port
+        url = 'https://127.0.0.1:%d' % port
         cacert = os.path.join(TEST_VAR_DIR, 'ca.crt')
 
         try:
@@ -175,9 +175,9 @@
 
     @mock.patch('sys.stderr')
     def test_v2_requests_valid_cert_no_key(self, __):
-        """Test VerifiedHTTPSConnection: absence of SSL key file."""
+        """Test client cert authentication fails without private key."""
         port = self.port
-        url = 'https://0.0.0.0:%d' % port
+        url = 'https://127.0.0.1:%d' % port
         cert_file = os.path.join(TEST_VAR_DIR, 'certificate.crt')
         cacert = os.path.join(TEST_VAR_DIR, 'ca.crt')
 
@@ -194,9 +194,9 @@
 
     @mock.patch('sys.stderr')
     def test_v2_requests_bad_cert(self, __):
-        """Test VerifiedHTTPSConnection: absence of SSL key file."""
+        """Test client cert authentication fails with bad certificate."""
         port = self.port
-        url = 'https://0.0.0.0:%d' % port
+        url = 'https://127.0.0.1:%d' % port
         cert_file = os.path.join(TEST_VAR_DIR, 'badcert.crt')
         cacert = os.path.join(TEST_VAR_DIR, 'ca.crt')
 
@@ -217,9 +217,9 @@
 
     @mock.patch('sys.stderr')
     def test_v2_requests_bad_ca(self, __):
-        """Test VerifiedHTTPSConnection: absence of SSL key file."""
+        """Test SSL verification fails with invalid CA certificate path."""
         port = self.port
-        url = 'https://0.0.0.0:%d' % port
+        url = 'https://127.0.0.1:%d' % port
         cacert = os.path.join(TEST_VAR_DIR, 'badca.crt')
 
         try:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/glanceclient/tests/unit/v2/test_images.py 
new/python_glanceclient-4.12.0/glanceclient/tests/unit/v2/test_images.py
--- old/python_glanceclient-4.11.0/glanceclient/tests/unit/v2/test_images.py    
2026-02-27 16:43:53.000000000 +0100
+++ new/python_glanceclient-4.12.0/glanceclient/tests/unit/v2/test_images.py    
2026-05-19 11:25:56.000000000 +0200
@@ -1323,6 +1323,36 @@
         except exc.HTTPForbidden:
             pass
 
+    def test_data_with_prefer(self):
+        image_id = '606b0e88-7c5a-4d54-b5bb-046105d4de6f'
+        prefer = ['store1', 'store2']
+        resp = utils.FakeResponse(headers={'content-length': '3'},
+                                  status_code=200)
+        self.controller.controller.http_client.get = mock.Mock(
+            return_value=(resp, 'CCC'))
+        self.controller.data(image_id, do_checksum=False,
+                             prefer=prefer)
+        # Check that the prefer parameter was passed as query parameters
+        calls = self.controller.controller.http_client.get.call_args_list
+        self.assertEqual(1, len(calls))
+        expected_call = calls[0]
+        self.assertIn('prefer=store1%2Cstore2', expected_call[0][0])
+        self.assertNotIn('stores', expected_call[0][0])
+
+    def test_data_without_stores(self):
+        image_id = '606b0e88-7c5a-4d54-b5bb-046105d4de6f'
+        resp = utils.FakeResponse(headers={'content-length': '3'},
+                                  status_code=200)
+        self.controller.controller.http_client.get = mock.Mock(
+            return_value=(resp, 'CCC'))
+        self.controller.data(image_id, do_checksum=False)
+        # Check that no query parameters were added
+        calls = self.controller.controller.http_client.get.call_args_list
+        self.assertEqual(1, len(calls))
+        expected_call = calls[0]
+        self.assertEqual('/v2/images/%s/file' % image_id, expected_call[0][0])
+        self.assertNotIn('?', expected_call[0][0])
+
     def test_update_replace_prop(self):
         image_id = '3a4560a1-e585-443e-9b39-553b46ec92d1'
         params = {'name': 'pong'}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/glanceclient/tests/unit/v2/test_shell_v2.py 
new/python_glanceclient-4.12.0/glanceclient/tests/unit/v2/test_shell_v2.py
--- old/python_glanceclient-4.11.0/glanceclient/tests/unit/v2/test_shell_v2.py  
2026-02-27 16:43:53.000000000 +0100
+++ new/python_glanceclient-4.12.0/glanceclient/tests/unit/v2/test_shell_v2.py  
2026-05-19 11:25:56.000000000 +0200
@@ -2774,7 +2774,7 @@
     def test_image_download(self):
         args = self._make_args(
             {'id': 'IMG-01', 'file': 'test', 'progress': True,
-             'allow_md5_fallback': False})
+             'allow_md5_fallback': False, 'prefer': None})
 
         with mock.patch.object(self.gc.images, 'data') as mocked_data, \
                 mock.patch.object(utils, '_extract_request_id'):
@@ -2783,7 +2783,8 @@
 
             test_shell.do_image_download(self.gc, args)
             mocked_data.assert_called_once_with('IMG-01',
-                                                allow_md5_fallback=False)
+                                                allow_md5_fallback=False,
+                                                prefer=None)
 
         # check that non-default value is being passed correctly
         args.allow_md5_fallback = True
@@ -2794,7 +2795,8 @@
 
             test_shell.do_image_download(self.gc, args)
             mocked_data.assert_called_once_with('IMG-01',
-                                                allow_md5_fallback=True)
+                                                allow_md5_fallback=True,
+                                                prefer=None)
 
     @mock.patch.object(utils, 'exit')
     @mock.patch('sys.stdout', autospec=True)
@@ -2802,7 +2804,7 @@
                                         mocked_utils_exit):
         # Indicate that no file name was given as command line argument
         args = self._make_args({'id': '1234', 'file': None, 'progress': False,
-                                'allow_md5_fallback': False})
+                                'allow_md5_fallback': False, 'prefer': None})
         # Indicate that no file is specified for output redirection
         mocked_stdout.isatty = lambda: True
         test_shell.do_image_download(self.gc, args)
@@ -2917,7 +2919,8 @@
                                                  mocked_stdout):
         args = self._make_args({'id': 'IMG-01', 'file': None,
                                 'progress': False,
-                                'allow_md5_fallback': False})
+                                'allow_md5_fallback': False,
+                                'prefer': None})
         mocked_stdout.isatty = lambda: False
         with mock.patch.object(self.gc.images, 'data') as mocked_data:
             mocked_data.side_effect = exc.HTTPForbidden
@@ -2935,7 +2938,8 @@
     def test_do_image_download_with_500(self, mocked_print_err, mocked_stdout):
         args = self._make_args({'id': 'IMG-01', 'file': None,
                                 'progress': False,
-                                'allow_md5_fallback': False})
+                                'allow_md5_fallback': False,
+                                'prefer': None})
         mocked_stdout.isatty = lambda: False
         with mock.patch.object(self.gc.images, 'data') as mocked_data:
             mocked_data.side_effect = exc.HTTPInternalServerError
@@ -2948,6 +2952,51 @@
             self.assertEqual(1, mocked_data.call_count)
             self.assertEqual(1, mocked_print_err.call_count)
 
+    def test_image_download_with_prefer(self):
+        args = self._make_args(
+            {'id': 'IMG-01', 'file': 'test', 'progress': False,
+             'allow_md5_fallback': False, 'prefer': 'store1,store2'})
+
+        with mock.patch.object(self.gc.images, 'data') as mocked_data, \
+                mock.patch.object(utils, '_extract_request_id'):
+            mocked_data.return_value = utils.RequestIdProxy(
+                [c for c in 'abcdef'])
+
+            test_shell.do_image_download(self.gc, args)
+            mocked_data.assert_called_once_with('IMG-01',
+                                                allow_md5_fallback=False,
+                                                prefer=['store1', 'store2'])
+
+    def test_image_download_with_prefer_empty_string(self):
+        args = self._make_args(
+            {'id': 'IMG-01', 'file': 'test', 'progress': False,
+             'allow_md5_fallback': False, 'prefer': ''})
+
+        with mock.patch.object(self.gc.images, 'data') as mocked_data, \
+                mock.patch.object(utils, '_extract_request_id'):
+            mocked_data.return_value = utils.RequestIdProxy(
+                [c for c in 'abcdef'])
+
+            test_shell.do_image_download(self.gc, args)
+            mocked_data.assert_called_once_with('IMG-01',
+                                                allow_md5_fallback=False,
+                                                prefer=None)
+
+    def test_image_download_without_prefer(self):
+        args = self._make_args(
+            {'id': 'IMG-01', 'file': 'test', 'progress': False,
+             'allow_md5_fallback': False, 'prefer': None})
+
+        with mock.patch.object(self.gc.images, 'data') as mocked_data, \
+                mock.patch.object(utils, '_extract_request_id'):
+            mocked_data.return_value = utils.RequestIdProxy(
+                [c for c in 'abcdef'])
+
+            test_shell.do_image_download(self.gc, args)
+            mocked_data.assert_called_once_with('IMG-01',
+                                                allow_md5_fallback=False,
+                                                prefer=None)
+
     def test_do_member_list(self):
         args = self._make_args({'image_id': 'IMG-01'})
         with mock.patch.object(self.gc.image_members, 'list') as mocked_list:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/glanceclient/tests/unit/var/ca.crt 
new/python_glanceclient-4.12.0/glanceclient/tests/unit/var/ca.crt
--- old/python_glanceclient-4.11.0/glanceclient/tests/unit/var/ca.crt   
2026-02-27 16:43:53.000000000 +0100
+++ new/python_glanceclient-4.12.0/glanceclient/tests/unit/var/ca.crt   
2026-05-19 11:25:56.000000000 +0200
@@ -1,36 +1,38 @@
+# DO NOT EDIT. This file is generated by tools/generate_test_certs.sh
 -----BEGIN CERTIFICATE-----
-MIIGVTCCBD2gAwIBAgIUEstxpjoCFDZo8K1Mmz7QIpYwSXIwDQYJKoZIhvcNAQEL
+MIIGYzCCBEugAwIBAgIUVH2HK/6/Au6Gf7JYvFM+Rps4J94wDQYJKoZIhvcNAQEL
 BQAwgbgxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhTdGF0ZSBDQTELMAkGA1UEBwwC
 Q0ExGTAXBgNVBAoMEE9wZW5TdGFjayBDQSBPcmcxGjAYBgNVBAsMEU9wZW5TdGFj
 ayBUZXN0IENBMS0wKwYDVQQDDCRPcGVuc3RhY2sgVGVzdCBDZXJ0aWZpY2F0ZSBB
-dXRob3JpdHkxIzAhBgkqhkiG9w0BCQEWFGFkbWluQGNhLmV4YW1wbGUuY29tMCAX
-DTIwMDQwNzEyMjYwMVoYDzI5OTkwMTAxMTIyNjAxWjCBuDELMAkGA1UEBhMCQVUx
-ETAPBgNVBAgMCFN0YXRlIENBMQswCQYDVQQHDAJDQTEZMBcGA1UECgwQT3BlblN0
-YWNrIENBIE9yZzEaMBgGA1UECwwRT3BlblN0YWNrIFRlc3QgQ0ExLTArBgNVBAMM
-JE9wZW5zdGFjayBUZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTEjMCEGCSqGSIb3
-DQEJARYUYWRtaW5AY2EuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4IC
-DwAwggIKAoICAQD6KfGpVSJsmhTgdbdovaaa3Qe4PeP+Dg9Y7muSggVQVlqUp3YB
-xUSo1RDoLyu1ci+KrNODr+kkD/Cbo8yJQCxqTzUpCw3tadFhUJOWvPaqdcYTA8R9
-p7Xjvw3Me7Q7xr4l0pCUIiz/kwxYxk+GCQyXzpXZm14zz+Qm8gz37eoW2jJfoyzA
-dB9Tp609Id7C6VHFCWZ2Zsa4+Ua/q+Pn7vLNJ61C2H5sfus8dtcGDzViDWwnWyHw
-spyR79rciV2yA3xeq09RvIx2SB1tc3S2Rxw7SmKeYcnkv6YplvhIG5QpErvp+URh
-De2wbbxzjFzJqFQO0Yh8IgTBIvQI02++lA8ZX84UDaxmrT92m8GfqQvb96em/H1k
-RKJTq0QqSC+BbGDeFxHkuOTJiOZm5Bnivpo0TAPwX6YqpadXARAFWw+fJiHCuFGr
-6ltD7zgRnx6SR5WNRNWmTZQNx7wC2Bm0cJ2ec0Asn+bl93RVloaNtbFJhkaN555G
-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
+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
+VAPX6MRWpQ==
 -----END CERTIFICATE-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/glanceclient/tests/unit/var/certificate.crt 
new/python_glanceclient-4.12.0/glanceclient/tests/unit/var/certificate.crt
--- old/python_glanceclient-4.11.0/glanceclient/tests/unit/var/certificate.crt  
2026-02-27 16:43:53.000000000 +0100
+++ new/python_glanceclient-4.12.0/glanceclient/tests/unit/var/certificate.crt  
2026-05-19 11:25:56.000000000 +0200
@@ -1,34 +1,36 @@
+# DO NOT EDIT. This file is generated by tools/generate_test_certs.sh
 -----BEGIN CERTIFICATE-----
-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
-9l4gwJ1ojNB2abF4LOEf5bU=
+MIIGIjCCBAqgAwIBAgIUa8IbpYTnevi57LCXS/7RHHMAhEQwDQYJKoZIhvcNAQEL
+BQAwgbgxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhTdGF0ZSBDQTELMAkGA1UEBwwC
+Q0ExGTAXBgNVBAoMEE9wZW5TdGFjayBDQSBPcmcxGjAYBgNVBAsMEU9wZW5TdGFj
+ayBUZXN0IENBMS0wKwYDVQQDDCRPcGVuc3RhY2sgVGVzdCBDZXJ0aWZpY2F0ZSBB
+dXRob3JpdHkxIzAhBgkqhkiG9w0BCQEWFGFkbWluQGNhLmV4YW1wbGUuY29tMB4X
+DTI2MDMwOTE1MjYyMloXDTM2MDMwNjE1MjYyMlowgYcxCzAJBgNVBAYTAkFVMREw
+DwYDVQQIDAhTdGF0ZSBDQTEPMA0GA1UEBwwGU3RhdGUxMRswGQYDVQQKDBJPcGVu
+U3RhY2sgVGVzdCBPcmcxHDAaBgNVBAsME09wZW5TdGFjayBUZXN0IFVuaXQxGTAX
+BgNVBAMMEHRlc3QuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQCcc5oW+VurGaz9KFHaO1DmQm2I7jUsYNsZK9+FnPJMxg836Zk3N1e9
+NDZiwQwXxgcU8yPEFdToqEJEedxi827iQW+yOvZKwCewhyTosqWblxhOUEHPmCRB
+6gJlMzxUj3JKoBbZyoBp1bjpMq8262fNVCxmwR7lMW6Fcmv1hrrtZY/BFA8h2j1R
+H8wIsSN+cQXyresXaP1hff67S/MKJWrQCcLL1MmBzD7ukbUaCaOmcCfhU10/2L99
+EtbZhc12DWxfWPdTs/wPwxPfWfuebv3T153B9sWSiIR2gClIFhm/moslbsDrlAmf
+hcPxLz6cUpWtqyk2kOXeoNiOgPD+om98Q9SunokPFI9uLjSy/wkbXbW6/C14ay8c
++044foTs6u2ACnKne9FricVB3p8zLhn5vtVAAE0koH8/aVUWkOD13lNLxIyz4kc2
+4qwIUOjc/YLcYoCZw91A7TRFQuHgm0A1GtBDIUg393fGsXMR8jXz6YOcI4tYobDK
+FXX3m8K79h3LgdKQismD/+hjZNOqgzYR7WH+6LMZw8ueXnBZzK0716iRC603dswU
+mUF5Ajz9YRWdGHn+MRdk3L4/p3YZzLkpLf1itgMxoyYsdyPyYktr+n7cmf8mqCBi
+5P+qhdGZqFh+1haZX/f8ugqN5Ev0LNUFQHBxGcPn4ascBXXW0N9gHwIDAQABo1Mw
+UTAPBgNVHREECDAGhwR/AAABMB0GA1UdDgQWBBS7bt0vNE5i4KM+nLEo+ZgdhHwN
+bTAfBgNVHSMEGDAWgBSdjBWCNzn38PrEmKGB2t7KLGKxHjANBgkqhkiG9w0BAQsF
+AAOCAgEAKJu6m7MhTUZx7g9jXltZJ/oI+EWDPAKocp5Z38ipfSa1pkmYc3wIWU1h
+FxBleFjbWFCHL6l3vHSjbm7ExfB9w+J9SA/7ZmztC2NiS7DimwPexlIAStESQhPp
+wBSSQWD3b/N+JYDmLykyDkNcgq+eNSs9Fn50mqnsgm1Q+UiFX13l97MnlH5S83RB
+KnsnR1PwL3a1XaiGPu8TlKabm40AGwBVVX2Y85jU/WMYTkZZJf/LtxNh6CLqcnQ2
+WPOyArKgXQVDhO9oeHfwK3K8FBzFtDHC+1TY962mC2yzqCv2eBbDeyk9hPhQQqUT
+VXUo49lW+ZryTRXVlHZYVFuMxedjDr6AMPf1y8m228p7H2rIiB+FWYPIv7xJ08T0
+hGT4NMMv+yrk3ZgabCmtPAqCVcmRTiPmygFu7QsantwI8o19/BFlE7xgOKNvIc7J
+ozNdBpcjXIYjtBi79dVSgqL3kzMJERUqu+uykLqruysGLMPcPtmAQxLOU/YuKray
+0j0QIiKN4XcArTR55L3hrREJKGw3wCgvYSahVoj04timrsIUnUl+YymjKAwykMP0
+/wZxdtLFpYOueAYrMgbOQOVfVxA4tfFD7hQuWr8VIRahDyjhb+rsH533xllDkDLB
+zHpRA3zDHjJXn8kP9DaNYo3tSBJQ9FzR6uQFexjqiTVwe4BBXi0=
 -----END CERTIFICATE-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/glanceclient/tests/unit/var/expired-cert.crt 
new/python_glanceclient-4.12.0/glanceclient/tests/unit/var/expired-cert.crt
--- old/python_glanceclient-4.11.0/glanceclient/tests/unit/var/expired-cert.crt 
2026-02-27 16:43:53.000000000 +0100
+++ new/python_glanceclient-4.12.0/glanceclient/tests/unit/var/expired-cert.crt 
2026-05-19 11:25:56.000000000 +0200
@@ -1,35 +1,36 @@
+# DO NOT EDIT. This file is generated by tools/generate_test_certs.sh
 -----BEGIN CERTIFICATE-----
-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
-l3+dFHnWayuCdqcFwIl3m8i8FupFihz9+A==
+MIIGIjCCBAqgAwIBAgIUa8IbpYTnevi57LCXS/7RHHMAhEUwDQYJKoZIhvcNAQEL
+BQAwgbgxCzAJBgNVBAYTAkFVMREwDwYDVQQIDAhTdGF0ZSBDQTELMAkGA1UEBwwC
+Q0ExGTAXBgNVBAoMEE9wZW5TdGFjayBDQSBPcmcxGjAYBgNVBAsMEU9wZW5TdGFj
+ayBUZXN0IENBMS0wKwYDVQQDDCRPcGVuc3RhY2sgVGVzdCBDZXJ0aWZpY2F0ZSBB
+dXRob3JpdHkxIzAhBgkqhkiG9w0BCQEWFGFkbWluQGNhLmV4YW1wbGUuY29tMB4X
+DTI2MDMwOTE1MjYyMloXDTI2MDMwOTE1MjYyMlowgYcxCzAJBgNVBAYTAkFVMREw
+DwYDVQQIDAhTdGF0ZSBDQTEPMA0GA1UEBwwGU3RhdGUxMRswGQYDVQQKDBJPcGVu
+U3RhY2sgVGVzdCBPcmcxHDAaBgNVBAsME09wZW5TdGFjayBUZXN0IFVuaXQxGTAX
+BgNVBAMMEHRlc3QuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
+ggIKAoICAQCcc5oW+VurGaz9KFHaO1DmQm2I7jUsYNsZK9+FnPJMxg836Zk3N1e9
+NDZiwQwXxgcU8yPEFdToqEJEedxi827iQW+yOvZKwCewhyTosqWblxhOUEHPmCRB
+6gJlMzxUj3JKoBbZyoBp1bjpMq8262fNVCxmwR7lMW6Fcmv1hrrtZY/BFA8h2j1R
+H8wIsSN+cQXyresXaP1hff67S/MKJWrQCcLL1MmBzD7ukbUaCaOmcCfhU10/2L99
+EtbZhc12DWxfWPdTs/wPwxPfWfuebv3T153B9sWSiIR2gClIFhm/moslbsDrlAmf
+hcPxLz6cUpWtqyk2kOXeoNiOgPD+om98Q9SunokPFI9uLjSy/wkbXbW6/C14ay8c
++044foTs6u2ACnKne9FricVB3p8zLhn5vtVAAE0koH8/aVUWkOD13lNLxIyz4kc2
+4qwIUOjc/YLcYoCZw91A7TRFQuHgm0A1GtBDIUg393fGsXMR8jXz6YOcI4tYobDK
+FXX3m8K79h3LgdKQismD/+hjZNOqgzYR7WH+6LMZw8ueXnBZzK0716iRC603dswU
+mUF5Ajz9YRWdGHn+MRdk3L4/p3YZzLkpLf1itgMxoyYsdyPyYktr+n7cmf8mqCBi
+5P+qhdGZqFh+1haZX/f8ugqN5Ev0LNUFQHBxGcPn4ascBXXW0N9gHwIDAQABo1Mw
+UTAPBgNVHREECDAGhwR/AAABMB0GA1UdDgQWBBS7bt0vNE5i4KM+nLEo+ZgdhHwN
+bTAfBgNVHSMEGDAWgBSdjBWCNzn38PrEmKGB2t7KLGKxHjANBgkqhkiG9w0BAQsF
+AAOCAgEAMCwZe3uUThBuhcI01Xzr1J08YqHB81WeORqRRmhQoSQZVawuwgMqFKzv
+GOEYGi1cYB1r+877C4vDjYtRQmtaLhVd09sgvNmTpdbPWfHooebSJcJzPhKt0Stb
+5oG5AMaM0GrcDPTXz7b81vGJPwKu0lYg/cDXmXbt176i7DHovEFXjA8COfAluc56
+rStc3wFRJ2mYP2j5iEWa7VjXrJGHSDhJAglTg1tuQkmePbcShThzw8OFpUdCBH3H
+s82Pr1aaH4NkgtG81sq+1EPHDOvBR8bjvuzxCDxIOkMMdDW7xjRwIdnT2uOE5kMj
+WJ1Z+AXZIsGvu8Wx2j4nwutuKc71ejHXUir3vrDmbGs7hqUdibu652bycw7Nc7a6
+D2JNrOWzJH7IuIyvR+NmL4IPcG3je3exfwmfz7612VkFv8Ek4cPWGi00zHwkE/Xz
+DMR/lFBc4R6Tpkj09JRWFGL2VDSzImcmaojIqQAapD40nDpZJ8TqOPwpaXeNcWIr
+xpEnMq4pLI9TUWFteXEMh5ij3CB2U0TAXeeowQ5nX2WNrAjnDov+mrSG79eNOBxl
+UblTCjfeytyciH4WQ2BDXkX06db2udeo7hSTABgnUYk26u7r2qPM4O1Zf/sWiDG4
+8oWEaI5553dfsp+sWW5UsC47N3YMUVCyrWX1/yX7Xc9RuuytF34=
 -----END CERTIFICATE-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/glanceclient/tests/unit/var/privatekey.key 
new/python_glanceclient-4.12.0/glanceclient/tests/unit/var/privatekey.key
--- old/python_glanceclient-4.11.0/glanceclient/tests/unit/var/privatekey.key   
2026-02-27 16:43:53.000000000 +0100
+++ new/python_glanceclient-4.12.0/glanceclient/tests/unit/var/privatekey.key   
2026-05-19 11:25:56.000000000 +0200
@@ -1,51 +1,53 @@
------BEGIN RSA PRIVATE KEY-----
-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
------END RSA PRIVATE KEY-----
+# DO NOT EDIT. This file is generated by tools/generate_test_certs.sh
+-----BEGIN PRIVATE KEY-----
+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
+blfeGXB9HbKj2/tdUT6DnyixuyDmv4DU
+-----END PRIVATE KEY-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/glanceclient/tests/unit/var/wildcard-certificate.crt
 
new/python_glanceclient-4.12.0/glanceclient/tests/unit/var/wildcard-certificate.crt
--- 
old/python_glanceclient-4.11.0/glanceclient/tests/unit/var/wildcard-certificate.crt
 2026-02-27 16:43:53.000000000 +0100
+++ 
new/python_glanceclient-4.12.0/glanceclient/tests/unit/var/wildcard-certificate.crt
 1970-01-01 01:00:00.000000000 +0100
@@ -1,61 +0,0 @@
-#Certificate:
-#    Data:
-#        Version: 1 (0x0)
-#        Serial Number: 13493453254446411258 (0xbb42603e589dedfa)
-#    Signature Algorithm: sha1WithRSAEncryption
-#        Issuer: C=US, ST=CA, L=State1, O=Openstack Test Org, OU=Openstack 
Test Unit, CN=*.pong.example.com/[email protected]
-#        Validity
-#            Not Before: Aug 21 17:29:18 2013 GMT
-#            Not After : Jul 28 17:29:18 2113 GMT
-#        Subject: C=US, ST=CA, L=State1, O=Openstack Test Org, OU=Openstack 
Test Unit, CN=*.pong.example.com/[email protected]
-#        Subject Public Key Info:
-#            Public Key Algorithm: rsaEncryption
-#                Public-Key: (4096 bit)
-#                Modulus:
-#                    00:d4:bb:3a:c4:a0:06:54:31:23:5d:b0:78:5a:be:
-#                    45:44:ae:a1:89:86:11:d8:ca:a8:33:b0:4f:f3:e1:
-#                    46:1e:85:a3:2a:9c:a4:e0:c2:14:34:4f:91:df:dc:
-#                    .
-#                    .
-#                    .
-#                Exponent: 65537 (0x10001)
-#    Signature Algorithm: sha1WithRSAEncryption
-#         9f:cc:08:5d:19:ee:54:31:a3:57:d7:3c:89:89:c0:69:41:dd:
-#         46:f8:73:68:ec:46:b9:fa:f5:df:f6:d9:58:35:d8:53:94:88:
-#         bd:36:a6:23:9e:0c:0d:89:62:35:91:49:b6:14:f4:43:69:3c:
-#         .
-#         .
-#         .
------BEGIN CERTIFICATE-----
-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
------END CERTIFICATE-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/glanceclient/tests/unit/var/wildcard-san-certificate.crt
 
new/python_glanceclient-4.12.0/glanceclient/tests/unit/var/wildcard-san-certificate.crt
--- 
old/python_glanceclient-4.11.0/glanceclient/tests/unit/var/wildcard-san-certificate.crt
     2026-02-27 16:43:53.000000000 +0100
+++ 
new/python_glanceclient-4.12.0/glanceclient/tests/unit/var/wildcard-san-certificate.crt
     1970-01-01 01:00:00.000000000 +0100
@@ -1,54 +0,0 @@
-#Certificate:
-#    Data:
-#        Version: 3 (0x2)
-#        Serial Number: 11990626514780340979 (0xa66743493fdcc2f3)
-#    Signature Algorithm: sha1WithRSAEncryption
-#        Issuer: C=US, ST=CA, L=State1, O=Openstack Test Org, OU=Openstack 
Test Unit, CN=0.0.0.0
-#        Validity
-#            Not Before: Dec 10 15:31:22 2013 GMT
-#            Not After : Nov 16 15:31:22 2113 GMT
-#        Subject: C=US, ST=CA, L=State1, O=Openstack Test Org, OU=Openstack 
Test Unit, CN=0.0.0.0
-#        Subject Public Key Info:
-#            Public Key Algorithm: rsaEncryption
-#                Public-Key: (2048 bit)
-#                Modulus:
-#                    00:ca:6b:07:73:53:24:45:74:05:a5:2a:27:bd:3e:
-#                    .
-#                    .
-#                    .
-#                Exponent: 65537 (0x10001)
-#        X509v3 extensions:
-#            X509v3 Key Usage:
-#                Key Encipherment, Data Encipherment
-#            X509v3 Extended Key Usage:
-#                TLS Web Server Authentication
-#            X509v3 Subject Alternative Name:
-#                DNS:foo.example.net, DNS:*.example.com
-#    Signature Algorithm: sha1WithRSAEncryption
-#         7e:41:69:da:f4:3c:06:d6:83:c6:f2:db:df:37:f1:ac:fa:f5:
-#         .
-#         .
-#         .
------BEGIN CERTIFICATE-----
-MIIDxDCCAqygAwIBAgIJAKZnQ0k/3MLzMA0GCSqGSIb3DQEBBQUAMHgxCzAJBgNV
-BAYTAlVTMQswCQYDVQQIEwJDQTEPMA0GA1UEBxMGU3RhdGUxMRswGQYDVQQKExJP
-cGVuc3RhY2sgVGVzdCBPcmcxHDAaBgNVBAsTE09wZW5zdGFjayBUZXN0IFVuaXQx
-EDAOBgNVBAMTBzAuMC4wLjAwIBcNMTMxMjEwMTUzMTIyWhgPMjExMzExMTYxNTMx
-MjJaMHgxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEPMA0GA1UEBxMGU3RhdGUx
-MRswGQYDVQQKExJPcGVuc3RhY2sgVGVzdCBPcmcxHDAaBgNVBAsTE09wZW5zdGFj
-ayBUZXN0IFVuaXQxEDAOBgNVBAMTBzAuMC4wLjAwggEiMA0GCSqGSIb3DQEBAQUA
-A4IBDwAwggEKAoIBAQDKawdzUyRFdAWlKie9Pn10j7frffN+z1gEMluK2CtDEwv9
-kbD4uS/Kz4dujfTx03mdyNfiMVlOM+YJm/qeLLSdJyFyvZ9Y3WmJ+vT2RGlMMhLd
-/wEnMRrTYLL39pwI6z+gyw+4D78Pyv/OXy02IA6WtVEefYSx1vmVngb3pL+iBzhO
-8CZXNI6lqrFhh+Hr4iMkYMtY1vTnwezAL6p64E/ZAFNPYCEJlacESTLQ4VZYniHc
-QTgnE1czlI1vxlIk1KDXAzUGeeopZecRih9qlTxtOpklqEciQEE+sHtPcvyvdRE9
-Bdyx5rNSALLIcXs0ViJE1RPlw3fjdBoDIOygqvX1AgMBAAGjTzBNMAsGA1UdDwQE
-AwIEMDATBgNVHSUEDDAKBggrBgEFBQcDATApBgNVHREEIjAggg9mb28uZXhhbXBs
-ZS5uZXSCDSouZXhhbXBsZS5jb20wDQYJKoZIhvcNAQEFBQADggEBAH5Badr0PAbW
-g8by29838az69Raul5IkpZQ5V3O1NaNNWxvmF1q8zFFqqGK5ktXJAwGiwnYEBb30
-Zfrr+eFIEERzBthSJkWlP8NG+2ooMyg50femp+asAvW+KYYefJW8KaXTsznMsAFy
-z1agcWVYVZ4H9PwunEYn/rM1krLEe4Cagsw5nmf8VqZg+hHtw930q8cRzgDsZdfA
-jVK6dWdmzmLCUTL1GKCeNriDw1jIeFvNufC+Q3orH7xBx4VL+NV5ORWdNY/B8q1b
-mFHdzbuZX6v39+2ww6aZqG2orfxUocc/5Ox6fXqenKPI3moeHS6Ktesq7sEQSJ6H
-QZFsTuT/124=
------END CERTIFICATE-----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python_glanceclient-4.11.0/glanceclient/v2/images.py 
new/python_glanceclient-4.12.0/glanceclient/v2/images.py
--- old/python_glanceclient-4.11.0/glanceclient/v2/images.py    2026-02-27 
16:43:53.000000000 +0100
+++ new/python_glanceclient-4.12.0/glanceclient/v2/images.py    2026-05-19 
11:25:56.000000000 +0200
@@ -216,7 +216,8 @@
                 'This operation is not supported by Glance.')
 
     @utils.add_req_id_to_object()
-    def data(self, image_id, do_checksum=True, allow_md5_fallback=False):
+    def data(self, image_id, do_checksum=True, allow_md5_fallback=False,
+             prefer=None):
         """Retrieve data of an image.
 
         When do_checksum is enabled, validation proceeds as follows:
@@ -240,6 +241,8 @@
         :param allow_md5_fallback:
             Use the MD5 checksum for validation if the algorithm specified by
             the image's 'os_hash_algo' property is not available
+        :param prefer: List of store identifiers suggesting the ordering of
+                      stores to try when downloading
         :returns: An iterable body or ``None``
         """
         if do_checksum:
@@ -252,6 +255,13 @@
             meta_hash_algo = image_meta.get('os_hash_algo', None)
 
         url = '/v2/images/%s/file' % image_id
+        params = {}
+        if prefer:
+            params['prefer'] = ','.join(prefer)
+
+        if params:
+            url = '%s?%s' % (url, urllib.parse.urlencode(params))
+
         resp, body = self.http_client.get(url)
         if resp.status_code == codes.no_content:
             return None, resp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python_glanceclient-4.11.0/glanceclient/v2/shell.py 
new/python_glanceclient-4.12.0/glanceclient/v2/shell.py
--- old/python_glanceclient-4.11.0/glanceclient/v2/shell.py     2026-02-27 
16:43:53.000000000 +0100
+++ new/python_glanceclient-4.12.0/glanceclient/v2/shell.py     2026-05-19 
11:25:56.000000000 +0200
@@ -678,6 +678,11 @@
 @utils.arg('id', metavar='<IMAGE_ID>', help=_('ID of image to download.'))
 @utils.arg('--progress', action='store_true', default=False,
            help=_('Show download progress bar.'))
[email protected]('--prefer', metavar='<STORES>',
+           help=_('Comma-separated list of store identifiers suggesting the '
+                  'ordering of stores to try when downloading the image. '
+                  'Store identifiers are site-specific and can be discovered '
+                  'using the "stores-info" command.'))
 def do_image_download(gc, args):
     """Download a specific image."""
     if sys.stdout.isatty() and (args.file is None):
@@ -686,9 +691,14 @@
                'downloaded image or redirect output to another source.')
         utils.exit(msg)
 
+    prefer = None
+    if args.prefer:
+        prefer = [s for s in (x.strip() for x in args.prefer.split(',')) if s]
+
     try:
         body = gc.images.data(args.id,
-                              allow_md5_fallback=args.allow_md5_fallback)
+                              allow_md5_fallback=args.allow_md5_fallback,
+                              prefer=prefer)
     except (exc.HTTPForbidden, exc.HTTPException) as e:
         msg = "Unable to download image '%s'. (%s)" % (args.id, e)
         utils.exit(msg)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/python_glanceclient.egg-info/PKG-INFO 
new/python_glanceclient-4.12.0/python_glanceclient.egg-info/PKG-INFO
--- old/python_glanceclient-4.11.0/python_glanceclient.egg-info/PKG-INFO        
2026-02-27 16:44:21.000000000 +0100
+++ new/python_glanceclient-4.12.0/python_glanceclient.egg-info/PKG-INFO        
2026-05-19 11:26:32.000000000 +0200
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: python-glanceclient
-Version: 4.11.0
+Version: 4.12.0
 Summary: OpenStack Image API Client Library
 Home-page: https://docs.openstack.org/python-glanceclient/latest/
 Author: OpenStack
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/python_glanceclient.egg-info/SOURCES.txt 
new/python_glanceclient-4.12.0/python_glanceclient.egg-info/SOURCES.txt
--- old/python_glanceclient-4.11.0/python_glanceclient.egg-info/SOURCES.txt     
2026-02-27 16:44:21.000000000 +0100
+++ new/python_glanceclient-4.12.0/python_glanceclient.egg-info/SOURCES.txt     
2026-05-19 11:26:32.000000000 +0200
@@ -31,7 +31,6 @@
 glanceclient/common/__init__.py
 glanceclient/common/exceptions.py
 glanceclient/common/http.py
-glanceclient/common/https.py
 glanceclient/common/progressbar.py
 glanceclient/common/utils.py
 glanceclient/tests/__init__.py
@@ -81,8 +80,6 @@
 glanceclient/tests/unit/var/certificate.crt
 glanceclient/tests/unit/var/expired-cert.crt
 glanceclient/tests/unit/var/privatekey.key
-glanceclient/tests/unit/var/wildcard-certificate.crt
-glanceclient/tests/unit/var/wildcard-san-certificate.crt
 glanceclient/v1/__init__.py
 glanceclient/v1/client.py
 glanceclient/v1/image_members.py
@@ -132,6 +129,7 @@
 releasenotes/notes/check-for-md5-59db8fd67870b214.yaml
 releasenotes/notes/copy-existing-image-619b7e6bc3394446.yaml
 releasenotes/notes/del_from_store-2d807c3038283907.yaml
+releasenotes/notes/download-from-suggested-stores.yaml
 releasenotes/notes/drop-py-2-7-f10417b8d1dd38fb.yaml
 releasenotes/notes/drop-python-3-6-and-3-7-0b299b4dc9673c6e.yaml
 releasenotes/notes/fix-undesirable-raw-python-error-66e3ddaca7b72ae2.yaml
@@ -160,6 +158,7 @@
 releasenotes/source/2024.2.rst
 releasenotes/source/2025.1.rst
 releasenotes/source/2025.2.rst
+releasenotes/source/2026.1.rst
 releasenotes/source/conf.py
 releasenotes/source/earlier.rst
 releasenotes/source/index.rst
@@ -181,5 +180,6 @@
 releasenotes/source/_static/.placeholder
 releasenotes/source/_templates/.placeholder
 tools/fix_ca_bundle.sh
+tools/generate_test_certs.sh
 tools/glance.bash_completion
 tools/with_venv.sh
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/python_glanceclient.egg-info/pbr.json 
new/python_glanceclient-4.12.0/python_glanceclient.egg-info/pbr.json
--- old/python_glanceclient-4.11.0/python_glanceclient.egg-info/pbr.json        
2026-02-27 16:44:21.000000000 +0100
+++ new/python_glanceclient-4.12.0/python_glanceclient.egg-info/pbr.json        
2026-05-19 11:26:32.000000000 +0200
@@ -1 +1 @@
-{"git_version": "b9e5198", "is_release": true}
\ No newline at end of file
+{"git_version": "0b06bb4", "is_release": true}
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/releasenotes/notes/download-from-suggested-stores.yaml
 
new/python_glanceclient-4.12.0/releasenotes/notes/download-from-suggested-stores.yaml
--- 
old/python_glanceclient-4.11.0/releasenotes/notes/download-from-suggested-stores.yaml
       1970-01-01 01:00:00.000000000 +0100
+++ 
new/python_glanceclient-4.12.0/releasenotes/notes/download-from-suggested-stores.yaml
       2026-05-19 11:25:56.000000000 +0200
@@ -0,0 +1,19 @@
+---
+features:
+  - |
+    The ``image-download`` command now supports the ``--prefer`` parameter
+    to suggest which stores to try when downloading images. The prefer
+    parameter accepts a comma-separated list of store identifiers.
+
+    If the image is not found in any of the specified stores, Glance will
+    fall back to trying all available stores.
+
+    Example usage:
+
+    .. code-block:: bash
+
+       glance image-download <image-id> --prefer store1,store2 --file 
image.qcow2
+
+    Store identifiers are site-specific and can be discovered using the
+    ``stores-info`` command.
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/releasenotes/source/2026.1.rst 
new/python_glanceclient-4.12.0/releasenotes/source/2026.1.rst
--- old/python_glanceclient-4.11.0/releasenotes/source/2026.1.rst       
1970-01-01 01:00:00.000000000 +0100
+++ new/python_glanceclient-4.12.0/releasenotes/source/2026.1.rst       
2026-05-19 11:25:56.000000000 +0200
@@ -0,0 +1,6 @@
+===========================
+2026.1 Series Release Notes
+===========================
+
+.. release-notes::
+   :branch: stable/2026.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/releasenotes/source/index.rst 
new/python_glanceclient-4.12.0/releasenotes/source/index.rst
--- old/python_glanceclient-4.11.0/releasenotes/source/index.rst        
2026-02-27 16:43:53.000000000 +0100
+++ new/python_glanceclient-4.12.0/releasenotes/source/index.rst        
2026-05-19 11:25:56.000000000 +0200
@@ -6,6 +6,7 @@
    :maxdepth: 1
 
    unreleased
+   2026.1
    2025.2
    2025.1
    2024.2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_glanceclient-4.11.0/tools/generate_test_certs.sh 
new/python_glanceclient-4.12.0/tools/generate_test_certs.sh
--- old/python_glanceclient-4.11.0/tools/generate_test_certs.sh 1970-01-01 
01:00:00.000000000 +0100
+++ new/python_glanceclient-4.12.0/tools/generate_test_certs.sh 2026-05-19 
11:25:56.000000000 +0200
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+#
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
+#    not use this file except in compliance with the License. You may obtain
+#    a copy of the License at
+#
+#         http://www.apache.org/licenses/LICENSE-2.0
+#
+#    Unless required by applicable law or agreed to in writing, software
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+#    License for the specific language governing permissions and limitations
+#    under the License.
+#
+# The script to generate the key file sand certificates files used in unit
+# tests. These files are saved in glanceclient/tests/unit/var .
+#
+pushd $(dirname -- "$0")/../glanceclient/tests/unit/var
+
+# Remove existing files
+rm *.key *.csr *.crt *.srl
+
+openssl genrsa -out ca.key 4096
+
+openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt \
+  -subj "/C=AU/ST=State CA/L=CA/O=OpenStack CA Org/OU=OpenStack Test 
CA/CN=Openstack Test Certificate Authority/[email protected]/" \
+  -addext "keyUsage=critical,digitalSignature,keyCertSign"
+
+openssl genrsa -out privatekey.key 4096
+
+openssl req -new -key privatekey.key -out server.csr \
+  -subj "/C=AU/ST=State CA/L=State1/O=OpenStack Test Org/OU=OpenStack Test 
Unit/CN=test.example.com/" \
+  -addext "subjectAltName=IP:127.0.0.1"
+openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
+  -out certificate.crt -days 3650 -sha256 -copy_extensions copyall
+
+openssl req -new -key privatekey.key -out expired-cert.csr \
+  -subj "/C=AU/ST=State CA/L=State1/O=OpenStack Test Org/OU=OpenStack Test 
Unit/CN=test.example.com/" \
+  -addext "subjectAltName=IP:127.0.0.1"
+openssl x509 -req -in expired-cert.csr -CA ca.crt -CAkey ca.key 
-CAcreateserial \
+  -out expired-cert.crt -days 0 -sha256 -copy_extensions copyall
+
+touch badcert.crt
+
+for f in badcert.crt ca.crt certificate.crt expired-cert.crt privatekey.key; do
+    if [ -f $f ]; then
+        sed -i '1i # DO NOT EDIT. This file is generated by 
tools/generate_test_certs.sh' $f
+    fi
+done
+
+# Remove unused files
+rm *.csr
+rm ca.key
+rm ca.srl
+
+popd

Reply via email to