blob: e9f0ca1e3ffcb7feeb3b1e4bb51a1b82e562592a [file] [log] [blame]
Kurt Taylor6a6f5be2013-04-02 18:53:47 -04001# Copyright 2013 IBM Corp.
Matthew Treinish72ea4422013-02-07 14:42:49 -05002# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
15
16import copy
Jordan Pittier9a573d92016-04-29 17:04:39 +020017import functools
Matthew Treinish72ea4422013-02-07 14:42:49 -050018
Doug Hellmann583ce2c2015-03-11 14:55:46 +000019from oslo_log import log as logging
Matthew Treinish21905512015-07-13 10:33:35 -040020from oslo_serialization import jsonutils as json
Matthew Treinish71426682015-04-23 11:19:38 -040021import six
Matthew Treinish89128142015-04-23 10:44:30 -040022from six.moves.urllib import parse as urllib
Masayuki Igawabfa07602015-01-20 18:47:17 +090023
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -080024from tempest.lib.common import rest_client
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050025from tempest.lib import exceptions as lib_exc
Matthew Treinish72ea4422013-02-07 14:42:49 -050026
Attila Fazekase72b7cd2013-03-26 18:34:21 +010027LOG = logging.getLogger(__name__)
Jordan Pittier9a573d92016-04-29 17:04:39 +020028CHUNKSIZE = 1024 * 64 # 64kB
Attila Fazekase72b7cd2013-03-26 18:34:21 +010029
Matthew Treinish72ea4422013-02-07 14:42:49 -050030
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -080031class ImagesClient(rest_client.RestClient):
Andrea Frittolicf40a862016-06-07 14:45:32 +090032 api_version = "v1"
Matthew Treinish72ea4422013-02-07 14:42:49 -050033
Matthew Treinish72ea4422013-02-07 14:42:49 -050034 def _image_meta_to_headers(self, fields):
35 headers = {}
36 fields_copy = copy.deepcopy(fields)
Attila Fazekase72b7cd2013-03-26 18:34:21 +010037 copy_from = fields_copy.pop('copy_from', None)
38 if copy_from is not None:
39 headers['x-glance-api-copy-from'] = copy_from
Matthew Treinish71426682015-04-23 11:19:38 -040040 for key, value in six.iteritems(fields_copy.pop('properties', {})):
Matthew Treinish72ea4422013-02-07 14:42:49 -050041 headers['x-image-meta-property-%s' % key] = str(value)
Matthew Treinish71426682015-04-23 11:19:38 -040042 for key, value in six.iteritems(fields_copy.pop('api', {})):
Attila Fazekase72b7cd2013-03-26 18:34:21 +010043 headers['x-glance-api-property-%s' % key] = str(value)
Matthew Treinish71426682015-04-23 11:19:38 -040044 for key, value in six.iteritems(fields_copy):
Matthew Treinish72ea4422013-02-07 14:42:49 -050045 headers['x-image-meta-%s' % key] = str(value)
46 return headers
47
Matthew Treinish72ea4422013-02-07 14:42:49 -050048 def _create_with_data(self, headers, data):
Jordan Pittier9a573d92016-04-29 17:04:39 +020049 # We are going to do chunked transfert, so split the input data
50 # info fixed-sized chunks.
51 headers['Content-Type'] = 'application/octet-stream'
52 data = iter(functools.partial(data.read, CHUNKSIZE), b'')
Andrea Frittolicf40a862016-06-07 14:45:32 +090053 resp, body = self.request('POST', 'images',
Jordan Pittier9a573d92016-04-29 17:04:39 +020054 headers=headers, body=data, chunked=True)
Andrea Frittolicf40a862016-06-07 14:45:32 +090055 self._error_checker('POST', 'images', headers, data, resp,
Jordan Pittier9a573d92016-04-29 17:04:39 +020056 body)
57 body = json.loads(body)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -080058 return rest_client.ResponseBody(resp, body)
Matthew Treinish72ea4422013-02-07 14:42:49 -050059
60 def _update_with_data(self, image_id, headers, data):
Jordan Pittier9a573d92016-04-29 17:04:39 +020061 # We are going to do chunked transfert, so split the input data
62 # info fixed-sized chunks.
63 headers['Content-Type'] = 'application/octet-stream'
64 data = iter(functools.partial(data.read, CHUNKSIZE), b'')
Andrea Frittolicf40a862016-06-07 14:45:32 +090065 url = 'images/%s' % image_id
Jordan Pittier9a573d92016-04-29 17:04:39 +020066 resp, body = self.request('PUT', url, headers=headers,
67 body=data, chunked=True)
Matthew Treinish72ea4422013-02-07 14:42:49 -050068 self._error_checker('PUT', url, headers, data,
Jordan Pittier9a573d92016-04-29 17:04:39 +020069 resp, body)
70 body = json.loads(body)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -080071 return rest_client.ResponseBody(resp, body)
Matthew Treinish72ea4422013-02-07 14:42:49 -050072
Andrea Frittoli8bbdb162014-01-06 11:06:13 +000073 @property
74 def http(self):
75 if self._http is None:
Masayuki Igawabc7e1892015-03-03 11:46:48 +090076 self._http = self._get_http()
Andrea Frittoli8bbdb162014-01-06 11:06:13 +000077 return self._http
78
Ghanshyam86a31e12015-12-16 15:42:47 +090079 def create_image(self, **kwargs):
Matthew Treinish72ea4422013-02-07 14:42:49 -050080 headers = {}
Ghanshyam86a31e12015-12-16 15:42:47 +090081 data = kwargs.pop('data', None)
82 headers.update(self._image_meta_to_headers(kwargs))
Matthew Treinish72ea4422013-02-07 14:42:49 -050083
Ghanshyam86a31e12015-12-16 15:42:47 +090084 if data is not None:
85 return self._create_with_data(headers, data)
Matthew Treinish72ea4422013-02-07 14:42:49 -050086
Andrea Frittolicf40a862016-06-07 14:45:32 +090087 resp, body = self.post('images', None, headers)
David Kranz9c3b3b62014-06-19 16:05:53 -040088 self.expected_success(201, resp.status)
Matthew Treinish72ea4422013-02-07 14:42:49 -050089 body = json.loads(body)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -080090 return rest_client.ResponseBody(resp, body)
Matthew Treinish72ea4422013-02-07 14:42:49 -050091
Ghanshyam86a31e12015-12-16 15:42:47 +090092 def update_image(self, image_id, **kwargs):
Matthew Treinish72ea4422013-02-07 14:42:49 -050093 headers = {}
Ghanshyam86a31e12015-12-16 15:42:47 +090094 data = kwargs.pop('data', None)
95 headers.update(self._image_meta_to_headers(kwargs))
Matthew Treinish72ea4422013-02-07 14:42:49 -050096
97 if data is not None:
98 return self._update_with_data(image_id, headers, data)
99
Andrea Frittolicf40a862016-06-07 14:45:32 +0900100 url = 'images/%s' % image_id
Ghanshyam86a31e12015-12-16 15:42:47 +0900101 resp, body = self.put(url, None, headers)
David Kranz9c3b3b62014-06-19 16:05:53 -0400102 self.expected_success(200, resp.status)
Matthew Treinish72ea4422013-02-07 14:42:49 -0500103 body = json.loads(body)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -0800104 return rest_client.ResponseBody(resp, body)
Matthew Treinish72ea4422013-02-07 14:42:49 -0500105
106 def delete_image(self, image_id):
Andrea Frittolicf40a862016-06-07 14:45:32 +0900107 url = 'images/%s' % image_id
David Kranz9c3b3b62014-06-19 16:05:53 -0400108 resp, body = self.delete(url)
109 self.expected_success(200, resp.status)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -0800110 return rest_client.ResponseBody(resp, body)
Matthew Treinish72ea4422013-02-07 14:42:49 -0500111
Ghanshyame60db482016-01-13 09:50:12 +0900112 def list_images(self, detail=False, **kwargs):
113 """Return a list of all images filtered by input parameters.
114
115 Available params: see http://developer.openstack.org/
116 api-ref-image-v1.html#listImage-v1
117
118 Most parameters except the following are passed to the API without
119 any changes.
120 :param changes_since: The name is changed to changes-since
121 """
Andrea Frittolicf40a862016-06-07 14:45:32 +0900122 url = 'images'
Matthew Treinish72ea4422013-02-07 14:42:49 -0500123
Ken'ichi Ohmichibcad2a22015-05-22 09:37:06 +0000124 if detail:
125 url += '/detail'
Attila Fazekas11795b52013-02-24 15:49:08 +0100126
Ghanshyame60db482016-01-13 09:50:12 +0900127 properties = kwargs.pop('properties', {})
128 for key, value in six.iteritems(properties):
129 kwargs['property-%s' % key] = value
ivan-zhuccc89462013-10-31 16:53:12 +0800130
Ghanshyame60db482016-01-13 09:50:12 +0900131 if kwargs.get('changes_since'):
132 kwargs['changes-since'] = kwargs.pop('changes_since')
ivan-zhud1bbe5d2013-12-29 18:32:46 +0800133
Attila Fazekas11795b52013-02-24 15:49:08 +0100134 if len(kwargs) > 0:
135 url += '?%s' % urllib.urlencode(kwargs)
Matthew Treinish72ea4422013-02-07 14:42:49 -0500136
137 resp, body = self.get(url)
David Kranz9c3b3b62014-06-19 16:05:53 -0400138 self.expected_success(200, resp.status)
Matthew Treinish72ea4422013-02-07 14:42:49 -0500139 body = json.loads(body)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -0800140 return rest_client.ResponseBody(resp, body)
Matthew Treinish72ea4422013-02-07 14:42:49 -0500141
Yaroslav Lobankov9d28a0f2016-04-19 15:05:57 +0300142 def check_image(self, image_id):
143 """Check image metadata."""
Andrea Frittolicf40a862016-06-07 14:45:32 +0900144 url = 'images/%s' % image_id
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700145 resp, body = self.head(url)
David Kranz9c3b3b62014-06-19 16:05:53 -0400146 self.expected_success(200, resp.status)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -0800147 return rest_client.ResponseBody(resp, body)
Attila Fazekase72b7cd2013-03-26 18:34:21 +0100148
Ken'ichi Ohmichi5d410762015-05-22 01:10:03 +0000149 def show_image(self, image_id):
Yaroslav Lobankov9d28a0f2016-04-19 15:05:57 +0300150 """Get image details plus the image itself."""
Andrea Frittolicf40a862016-06-07 14:45:32 +0900151 url = 'images/%s' % image_id
Attila Fazekase72b7cd2013-03-26 18:34:21 +0100152 resp, body = self.get(url)
David Kranz9c3b3b62014-06-19 16:05:53 -0400153 self.expected_success(200, resp.status)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -0800154 return rest_client.ResponseBodyData(resp, body)
Matthew Treinish72ea4422013-02-07 14:42:49 -0500155
156 def is_resource_deleted(self, id):
157 try:
Ken'ichi Ohmichi01151e82016-06-10 11:19:52 -0700158 resp = self.check_image(id)
159 if resp.response["x-image-meta-status"] == 'deleted':
Joshua White95dcef22016-04-11 06:17:42 -0700160 return True
Masayuki Igawabfa07602015-01-20 18:47:17 +0900161 except lib_exc.NotFound:
Matthew Treinish72ea4422013-02-07 14:42:49 -0500162 return True
163 return False
Matthew Treinishfa23cf82013-03-06 14:23:02 -0500164
Matt Riedemannd2b96512014-10-13 10:18:16 -0700165 @property
166 def resource_type(self):
167 """Returns the primary type of resource this client works with."""
168 return 'image_meta'