summaryrefslogtreecommitdiff
path: root/glanceclient/v2
diff options
context:
space:
mode:
Diffstat (limited to 'glanceclient/v2')
-rw-r--r--glanceclient/v2/images.py63
-rw-r--r--glanceclient/v2/shell.py14
2 files changed, 70 insertions, 7 deletions
diff --git a/glanceclient/v2/images.py b/glanceclient/v2/images.py
index be804a2..09d46b9 100644
--- a/glanceclient/v2/images.py
+++ b/glanceclient/v2/images.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import hashlib
import json
from oslo_utils import encodeutils
from requests import codes
@@ -197,13 +198,39 @@ class Controller(object):
return self._get(image_id)
@utils.add_req_id_to_object()
- def data(self, image_id, do_checksum=True):
+ def data(self, image_id, do_checksum=True, allow_md5_fallback=False):
"""Retrieve data of an image.
- :param image_id: ID of the image to download.
- :param do_checksum: Enable/disable checksum validation.
- :returns: An iterable body or None
+ When do_checksum is enabled, validation proceeds as follows:
+
+ 1. if the image has a 'os_hash_value' property, the algorithm
+ specified in the image's 'os_hash_algo' property will be used
+ to validate against the 'os_hash_value' value. If the
+ specified hash algorithm is not available AND allow_md5_fallback
+ is True, then continue to step #2
+ 2. else if the image has a checksum property, MD5 is used to
+ validate against the 'checksum' value
+ 3. else if the download response has a 'content-md5' header, MD5
+ is used to validate against the header value
+ 4. if none of 1-3 obtain, the data is **not validated** (this is
+ compatible with legacy behavior)
+
+ :param image_id: ID of the image to download
+ :param do_checksum: Enable/disable checksum validation
+ :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
+ :returns: An iterable body or ``None``
"""
+ if do_checksum:
+ # doing this first to prevent race condition if image record
+ # is deleted during the image download
+ url = '/v2/images/%s' % image_id
+ resp, image_meta = self.http_client.get(url)
+ meta_checksum = image_meta.get('checksum', None)
+ meta_hash_value = image_meta.get('os_hash_value', None)
+ meta_hash_algo = image_meta.get('os_hash_algo', None)
+
url = '/v2/images/%s/file' % image_id
resp, body = self.http_client.get(url)
if resp.status_code == codes.no_content:
@@ -212,8 +239,32 @@ class Controller(object):
checksum = resp.headers.get('content-md5', None)
content_length = int(resp.headers.get('content-length', 0))
- if do_checksum and checksum is not None:
- body = utils.integrity_iter(body, checksum)
+ check_md5sum = do_checksum
+ if do_checksum and meta_hash_value is not None:
+ try:
+ hasher = hashlib.new(str(meta_hash_algo))
+ body = utils.serious_integrity_iter(body,
+ hasher,
+ meta_hash_value)
+ check_md5sum = False
+ except ValueError as ve:
+ if (str(ve).startswith('unsupported hash type') and
+ allow_md5_fallback):
+ check_md5sum = True
+ else:
+ raise
+
+ if do_checksum and check_md5sum:
+ if meta_checksum is not None:
+ body = utils.integrity_iter(body, meta_checksum)
+ elif checksum is not None:
+ body = utils.integrity_iter(body, checksum)
+ else:
+ # NOTE(rosmaita): this preserves legacy behavior to return the
+ # image data when checksumming is requested but there's no
+ # 'content-md5' header in the response. Just want to make it
+ # clear that we're doing this on purpose.
+ pass
return utils.IterableWithLength(body, content_length), resp
diff --git a/glanceclient/v2/shell.py b/glanceclient/v2/shell.py
index aaa85bb..54dd789 100644
--- a/glanceclient/v2/shell.py
+++ b/glanceclient/v2/shell.py
@@ -490,6 +490,17 @@ def do_stores_info(gc, args):
utils.print_dict(stores_info)
+@utils.arg('--allow-md5-fallback', action='store_true',
+ default=utils.env('OS_IMAGE_ALLOW_MD5_FALLBACK', default=False),
+ help=_('If os_hash_algo and os_hash_value properties are available '
+ 'on the image, they will be used to validate the downloaded '
+ 'image data. If the indicated secure hash algorithm is not '
+ 'available on the client, the download will fail. Use this '
+ 'flag to indicate that in such a case the legacy MD5 image '
+ 'checksum should be used to validate the downloaded data. '
+ 'You can also set the enviroment variable '
+ 'OS_IMAGE_ALLOW_MD5_FALLBACK to any value to activate this '
+ 'option.'))
@utils.arg('--file', metavar='<FILE>',
help=_('Local file to save downloaded image data to. '
'If this is not specified and there is no redirection '
@@ -506,7 +517,8 @@ def do_image_download(gc, args):
utils.exit(msg)
try:
- body = gc.images.data(args.id)
+ body = gc.images.data(args.id,
+ allow_md5_fallback=args.allow_md5_fallback)
except (exc.HTTPForbidden, exc.HTTPException) as e:
msg = "Unable to download image '%s'. (%s)" % (args.id, e)
utils.exit(msg)