
Add a new module that contain generic wrapper for file and iterator, which are used to wrap image to upload and the request body iterator in upload and download cases repectively, to show and advance a pretty progress bar when this laters are consumed, The progress bar is triggered by adding a --progress command line argument to commands: image-create, image-download or image-update. Change-Id: I2ba42fd0c58f4fa087adb568ec3f08246cae3759 bug fix: LP#1112309 blueprint: progressbar-when-uploading
145 lines
5.0 KiB
Python
145 lines
5.0 KiB
Python
# Copyright 2012 OpenStack LLC.
|
|
# 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 urllib
|
|
|
|
import warlock
|
|
|
|
from glanceclient.common import utils
|
|
from glanceclient.openstack.common import strutils
|
|
|
|
DEFAULT_PAGE_SIZE = 100
|
|
|
|
|
|
class Controller(object):
|
|
def __init__(self, http_client, model):
|
|
self.http_client = http_client
|
|
self.model = model
|
|
|
|
def list(self, **kwargs):
|
|
"""Retrieve a listing of Image objects
|
|
|
|
:param page_size: Number of images to request in each paginated request
|
|
:returns generator over list of Images
|
|
"""
|
|
def paginate(url):
|
|
resp, body = self.http_client.json_request('GET', url)
|
|
for image in body['images']:
|
|
yield image
|
|
try:
|
|
next_url = body['next']
|
|
except KeyError:
|
|
return
|
|
else:
|
|
for image in paginate(next_url):
|
|
yield image
|
|
|
|
filters = kwargs.get('filters', {})
|
|
|
|
if not kwargs.get('page_size'):
|
|
filters['limit'] = DEFAULT_PAGE_SIZE
|
|
else:
|
|
filters['limit'] = kwargs['page_size']
|
|
|
|
for param, value in filters.iteritems():
|
|
if isinstance(value, basestring):
|
|
filters[param] = strutils.safe_encode(value)
|
|
|
|
url = '/v2/images?%s' % urllib.urlencode(filters)
|
|
|
|
for image in paginate(url):
|
|
#NOTE(bcwaldon): remove 'self' for now until we have an elegant
|
|
# way to pass it into the model constructor without conflict
|
|
image.pop('self', None)
|
|
yield self.model(**image)
|
|
|
|
def get(self, image_id):
|
|
url = '/v2/images/%s' % image_id
|
|
resp, body = self.http_client.json_request('GET', url)
|
|
#NOTE(bcwaldon): remove 'self' for now until we have an elegant
|
|
# way to pass it into the model constructor without conflict
|
|
body.pop('self', None)
|
|
return self.model(**body)
|
|
|
|
def data(self, image_id, do_checksum=True):
|
|
"""
|
|
Retrieve data of an image.
|
|
|
|
:param image_id: ID of the image to download.
|
|
:param do_checksum: Enable/disable checksum validation.
|
|
"""
|
|
url = '/v2/images/%s/file' % image_id
|
|
resp, body = self.http_client.raw_request('GET', url)
|
|
checksum = resp.getheader('content-md5', None)
|
|
if do_checksum and checksum is not None:
|
|
body.set_checksum(checksum)
|
|
return body
|
|
|
|
def upload(self, image_id, image_data):
|
|
"""
|
|
Upload the data for an image.
|
|
|
|
:param image_id: ID of the image to upload data for.
|
|
:param image_data: File-like object supplying the data to upload.
|
|
"""
|
|
url = '/v2/images/%s/file' % image_id
|
|
hdrs = {'Content-Type': 'application/octet-stream'}
|
|
self.http_client.raw_request('PUT', url,
|
|
headers=hdrs,
|
|
body=image_data)
|
|
|
|
def delete(self, image_id):
|
|
"""Delete an image."""
|
|
self.http_client.json_request('DELETE', 'v2/images/%s' % image_id)
|
|
|
|
def create(self, **kwargs):
|
|
"""Create an image."""
|
|
url = '/v2/images'
|
|
|
|
image = self.model()
|
|
for (key, value) in kwargs.items():
|
|
try:
|
|
setattr(image, key, value)
|
|
except warlock.InvalidOperation, e:
|
|
raise TypeError(utils.exception_to_str(e))
|
|
|
|
resp, body = self.http_client.json_request('POST', url, body=image)
|
|
#NOTE(esheffield): remove 'self' for now until we have an elegant
|
|
# way to pass it into the model constructor without conflict
|
|
body.pop('self', None)
|
|
return self.model(**body)
|
|
|
|
def update(self, image_id, **kwargs):
|
|
"""
|
|
Update attributes of an image.
|
|
|
|
:param image_id: ID of the image to modify.
|
|
:param **kwargs: Image attribute names and their new values.
|
|
"""
|
|
image = self.get(image_id)
|
|
for (key, value) in kwargs.items():
|
|
setattr(image, key, value)
|
|
|
|
url = '/v2/images/%s' % image_id
|
|
hdrs = {'Content-Type': 'application/openstack-images-v2.0-json-patch'}
|
|
self.http_client.raw_request('PATCH', url,
|
|
headers=hdrs,
|
|
body=image.patch)
|
|
|
|
#NOTE(bcwaldon): calling image.patch doesn't clear the changes, so
|
|
# we need to fetch the image again to get a clean history. This is
|
|
# an obvious optimization for warlock
|
|
return self.get(image_id)
|