blob: ae6ce25ded9b84e4fbf2bfaf53b3833251f4d829 [file] [log] [blame]
Kurt Taylor6a6f5be2013-04-02 18:53:47 -04001# Copyright 2013 IBM Corp.
Matthew Treinisha62347f2013-03-01 16:37:30 -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
Jordan Pittier9a573d92016-04-29 17:04:39 +020016import functools
songwenping99d6e002021-01-05 03:07:46 +000017from urllib import parse as urllib
Jordan Pittier9a573d92016-04-29 17:04:39 +020018
Matthew Treinish21905512015-07-13 10:33:35 -040019from oslo_serialization import jsonutils as json
Matthew Treinisha62347f2013-03-01 16:37:30 -050020
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -080021from tempest.lib.common import rest_client
Andrea Frittoli (andreaf)db9672e2016-02-23 14:07:24 -050022from tempest.lib import exceptions as lib_exc
Matthew Treinish684d8992014-01-30 16:27:40 +000023
Jordan Pittier9a573d92016-04-29 17:04:39 +020024CHUNKSIZE = 1024 * 64 # 64kB
25
Matthew Treinisha62347f2013-03-01 16:37:30 -050026
Yaroslav Lobankov2fea4052016-04-19 15:05:57 +030027class ImagesClient(rest_client.RestClient):
Andrea Frittolicf40a862016-06-07 14:45:32 +090028 api_version = "v2"
Matthew Treinisha62347f2013-03-01 16:37:30 -050029
Sergey Nikitinc6b2ee82014-02-03 17:13:50 +040030 def update_image(self, image_id, patch):
Ken'ichi Ohmichi25b30162015-11-30 11:27:13 +000031 """Update an image.
32
OTSUKA, Yuanyingfaac5712016-09-15 13:53:55 +090033 For a full list of available parameters, please refer to the official
34 API reference:
Andreas Jaegerbf30ae72019-07-22 19:22:57 +020035 https://docs.openstack.org/api-ref/image/v2/#update-image
Ken'ichi Ohmichi25b30162015-11-30 11:27:13 +000036 """
Sergey Nikitinc6b2ee82014-02-03 17:13:50 +040037 data = json.dumps(patch)
Sergey Nikitinc6b2ee82014-02-03 17:13:50 +040038 headers = {"Content-Type": "application/openstack-images-v2.0"
39 "-json-patch"}
Andrea Frittolicf40a862016-06-07 14:45:32 +090040 resp, body = self.patch('images/%s' % image_id, data, headers)
David Kranz9c3b3b62014-06-19 16:05:53 -040041 self.expected_success(200, resp.status)
ghanshyam08a73f92015-08-31 17:32:49 +090042 body = json.loads(body)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -080043 return rest_client.ResponseBody(resp, body)
Sergey Nikitinc6b2ee82014-02-03 17:13:50 +040044
Ken'ichi Ohmichi25b30162015-11-30 11:27:13 +000045 def create_image(self, **kwargs):
46 """Create an image.
Matthew Treinishce3ef922013-03-11 14:02:46 -040047
OTSUKA, Yuanyingfaac5712016-09-15 13:53:55 +090048 For a full list of available parameters, please refer to the official
49 API reference:
Andreas Jaegerbf30ae72019-07-22 19:22:57 +020050 https://docs.openstack.org/api-ref/image/v2/#create-image
Ken'ichi Ohmichi25b30162015-11-30 11:27:13 +000051 """
52 data = json.dumps(kwargs)
Andrea Frittolicf40a862016-06-07 14:45:32 +090053 resp, body = self.post('images', data)
David Kranz9c3b3b62014-06-19 16:05:53 -040054 self.expected_success(201, resp.status)
Matthew Treinisha62347f2013-03-01 16:37:30 -050055 body = json.loads(body)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -080056 return rest_client.ResponseBody(resp, body)
Matthew Treinisha62347f2013-03-01 16:37:30 -050057
bkopilov81aaae72015-05-15 23:46:25 +030058 def deactivate_image(self, image_id):
Anthony Washington4dcd0022016-06-30 18:24:46 +000059 """Deactivate image.
60
OTSUKA, Yuanyingfaac5712016-09-15 13:53:55 +090061 For a full list of available parameters, please refer to the official
62 API reference:
Andreas Jaegerbf30ae72019-07-22 19:22:57 +020063 https://docs.openstack.org/api-ref/image/v2/#deactivate-image
OTSUKA, Yuanyingfaac5712016-09-15 13:53:55 +090064 """
Andrea Frittolicf40a862016-06-07 14:45:32 +090065 url = 'images/%s/actions/deactivate' % image_id
bkopilov81aaae72015-05-15 23:46:25 +030066 resp, body = self.post(url, None)
67 self.expected_success(204, resp.status)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -080068 return rest_client.ResponseBody(resp, body)
bkopilov81aaae72015-05-15 23:46:25 +030069
70 def reactivate_image(self, image_id):
Anthony Washington4dcd0022016-06-30 18:24:46 +000071 """Reactivate image.
72
OTSUKA, Yuanyingfaac5712016-09-15 13:53:55 +090073 For a full list of available parameters, please refer to the official
74 API reference:
Andreas Jaegerbf30ae72019-07-22 19:22:57 +020075 https://docs.openstack.org/api-ref/image/v2/#reactivate-image
OTSUKA, Yuanyingfaac5712016-09-15 13:53:55 +090076 """
Andrea Frittolicf40a862016-06-07 14:45:32 +090077 url = 'images/%s/actions/reactivate' % image_id
bkopilov81aaae72015-05-15 23:46:25 +030078 resp, body = self.post(url, None)
79 self.expected_success(204, resp.status)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -080080 return rest_client.ResponseBody(resp, body)
bkopilov81aaae72015-05-15 23:46:25 +030081
Matthew Treinisha62347f2013-03-01 16:37:30 -050082 def delete_image(self, image_id):
Anthony Washington4dcd0022016-06-30 18:24:46 +000083 """Delete image.
84
OTSUKA, Yuanyingfaac5712016-09-15 13:53:55 +090085 For a full list of available parameters, please refer to the official
86 API reference:
Andreas Jaegerbf30ae72019-07-22 19:22:57 +020087 https://docs.openstack.org/api-ref/image/v2/#delete-image
Anthony Washington4dcd0022016-06-30 18:24:46 +000088 """
Andrea Frittolicf40a862016-06-07 14:45:32 +090089 url = 'images/%s' % image_id
David Kranz9c3b3b62014-06-19 16:05:53 -040090 resp, _ = self.delete(url)
91 self.expected_success(204, resp.status)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -080092 return rest_client.ResponseBody(resp)
Matthew Treinisha62347f2013-03-01 16:37:30 -050093
Ken'ichi Ohmichie3acc122015-05-22 00:32:54 +000094 def list_images(self, params=None):
Anthony Washington4dcd0022016-06-30 18:24:46 +000095 """List images.
96
OTSUKA, Yuanyingfaac5712016-09-15 13:53:55 +090097 For a full list of available parameters, please refer to the official
98 API reference:
Andreas Jaegerbf30ae72019-07-22 19:22:57 +020099 https://docs.openstack.org/api-ref/image/v2/#list-images
Anthony Washington4dcd0022016-06-30 18:24:46 +0000100 """
Andrea Frittolicf40a862016-06-07 14:45:32 +0900101 url = 'images'
Matthew Treinisha62347f2013-03-01 16:37:30 -0500102
103 if params:
104 url += '?%s' % urllib.urlencode(params)
105
106 resp, body = self.get(url)
David Kranz9c3b3b62014-06-19 16:05:53 -0400107 self.expected_success(200, resp.status)
Matthew Treinisha62347f2013-03-01 16:37:30 -0500108 body = json.loads(body)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -0800109 return rest_client.ResponseBody(resp, body)
Matthew Treinisha62347f2013-03-01 16:37:30 -0500110
Ken'ichi Ohmichi5d410762015-05-22 01:10:03 +0000111 def show_image(self, image_id):
Anthony Washington4dcd0022016-06-30 18:24:46 +0000112 """Show image details.
113
OTSUKA, Yuanyingfaac5712016-09-15 13:53:55 +0900114 For a full list of available parameters, please refer to the official
115 API reference:
Andreas Jaegerbf30ae72019-07-22 19:22:57 +0200116 https://docs.openstack.org/api-ref/image/v2/#show-image
Anthony Washington4dcd0022016-06-30 18:24:46 +0000117 """
Andrea Frittolicf40a862016-06-07 14:45:32 +0900118 url = 'images/%s' % image_id
Matthew Treinisha62347f2013-03-01 16:37:30 -0500119 resp, body = self.get(url)
David Kranz9c3b3b62014-06-19 16:05:53 -0400120 self.expected_success(200, resp.status)
Matthew Treinisha62347f2013-03-01 16:37:30 -0500121 body = json.loads(body)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -0800122 return rest_client.ResponseBody(resp, body)
Matthew Treinisha62347f2013-03-01 16:37:30 -0500123
Dan Smith7bde7bf2021-02-15 08:44:47 -0800124 def show_image_tasks(self, image_id):
125 """Show image tasks."""
126 url = 'images/%s/tasks' % image_id
127 resp, body = self.get(url)
128 self.expected_success(200, resp.status)
129 body = json.loads(body)
130 return rest_client.ResponseBody(resp, body)
131
Matthew Treinisha62347f2013-03-01 16:37:30 -0500132 def is_resource_deleted(self, id):
133 try:
Ken'ichi Ohmichi5d410762015-05-22 01:10:03 +0000134 self.show_image(id)
Masayuki Igawabfa07602015-01-20 18:47:17 +0900135 except lib_exc.NotFound:
Matthew Treinisha62347f2013-03-01 16:37:30 -0500136 return True
137 return False
138
Abhishek Kekane7cff1302020-07-16 10:30:13 +0000139 def is_resource_active(self, id):
140 try:
141 image = self.show_image(id)
142 if image['status'] != 'active':
143 return False
144 except lib_exc.NotFound:
145 return False
146 return True
147
Matt Riedemannd2b96512014-10-13 10:18:16 -0700148 @property
149 def resource_type(self):
150 """Returns the primary type of resource this client works with."""
151 return 'image'
152
Ken'ichi Ohmichi66494e92015-06-08 04:28:10 +0000153 def store_image_file(self, image_id, data):
Anthony Washington4dcd0022016-06-30 18:24:46 +0000154 """Upload binary image data.
155
OTSUKA, Yuanyingfaac5712016-09-15 13:53:55 +0900156 For a full list of available parameters, please refer to the official
157 API reference:
Andreas Jaegerbf30ae72019-07-22 19:22:57 +0200158 https://docs.openstack.org/api-ref/image/v2/#upload-binary-image-data
Anthony Washington4dcd0022016-06-30 18:24:46 +0000159 """
Andrea Frittolicf40a862016-06-07 14:45:32 +0900160 url = 'images/%s/file' % image_id
Jordan Pittier9a573d92016-04-29 17:04:39 +0200161
162 # We are going to do chunked transfert, so split the input data
163 # info fixed-sized chunks.
Matthew Treinisha62347f2013-03-01 16:37:30 -0500164 headers = {'Content-Type': 'application/octet-stream'}
Jordan Pittier9a573d92016-04-29 17:04:39 +0200165 data = iter(functools.partial(data.read, CHUNKSIZE), b'')
166
167 resp, body = self.request('PUT', url, headers=headers,
168 body=data, chunked=True)
David Kranz9c3b3b62014-06-19 16:05:53 -0400169 self.expected_success(204, resp.status)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -0800170 return rest_client.ResponseBody(resp, body)
Matthew Treinisha62347f2013-03-01 16:37:30 -0500171
Abhishek Kekane7cff1302020-07-16 10:30:13 +0000172 def stage_image_file(self, image_id, data):
173 """Upload binary image data to staging area.
174
175 For a full list of available parameters, please refer to the official
176 API reference (stage API:
177 https://docs.openstack.org/api-ref/image/v2/#interoperable-image-import
178 """
179 url = 'images/%s/stage' % image_id
180
181 # We are going to do chunked transfer, so split the input data
182 # info fixed-sized chunks.
183 headers = {'Content-Type': 'application/octet-stream'}
184 data = iter(functools.partial(data.read, CHUNKSIZE), b'')
185
186 resp, body = self.request('PUT', url, headers=headers,
187 body=data, chunked=True)
188 self.expected_success(204, resp.status)
189 return rest_client.ResponseBody(resp, body)
190
191 def info_import(self):
192 """Return information about server-supported import methods."""
193 url = 'info/import'
194 resp, body = self.get(url)
195
196 self.expected_success(200, resp.status)
197 body = json.loads(body)
198 return rest_client.ResponseBody(resp, body)
199
200 def info_stores(self):
201 """Return information about server-supported stores."""
202 url = 'info/stores'
203 resp, body = self.get(url)
204 body = json.loads(body)
205 return rest_client.ResponseBody(resp, body)
206
207 def image_import(self, image_id, method='glance-direct',
208 all_stores_must_succeed=None, all_stores=True,
Dan Smith5ac5c182022-08-09 09:42:59 -0700209 stores=None, import_params=None):
Abhishek Kekane7cff1302020-07-16 10:30:13 +0000210 """Import data from staging area to glance store.
211
212 For a full list of available parameters, please refer to the official
213 API reference (stage API:
214 https://docs.openstack.org/api-ref/image/v2/#interoperable-image-import
215
216 :param method: The import method (i.e. glance-direct) to use
217 :param all_stores_must_succeed: Boolean indicating if all store imports
218 must succeed for the import to be
219 considered successful. Must be None if
220 server does not support multistore.
221 :param all_stores: Boolean indicating if image should be imported to
222 all available stores (incompatible with stores)
223 :param stores: A list of destination store names for the import. Must
224 be None if server does not support multistore.
Dan Smith5ac5c182022-08-09 09:42:59 -0700225 :param import_params: A dict of import method parameters
Abhishek Kekane7cff1302020-07-16 10:30:13 +0000226 """
227 url = 'images/%s/import' % image_id
Dan Smith5ac5c182022-08-09 09:42:59 -0700228 if import_params is None:
229 import_params = {}
Abhishek Kekane7cff1302020-07-16 10:30:13 +0000230 data = {
231 "method": {
232 "name": method
233 },
234 }
235 if stores is not None:
236 data["stores"] = stores
237 else:
238 data["all_stores"] = all_stores
239
240 if all_stores_must_succeed is not None:
241 data['all_stores_must_succeed'] = all_stores_must_succeed
Dan Smith5ac5c182022-08-09 09:42:59 -0700242 if import_params:
243 data['method'].update(import_params)
Abhishek Kekane7cff1302020-07-16 10:30:13 +0000244 data = json.dumps(data)
245 headers = {'Content-Type': 'application/json'}
246 resp, _ = self.post(url, data, headers=headers)
247
248 self.expected_success(202, resp.status)
249 return rest_client.ResponseBody(resp)
250
Ken'ichi Ohmichi6bd6f202015-11-20 05:29:38 +0000251 def show_image_file(self, image_id):
Anthony Washington4dcd0022016-06-30 18:24:46 +0000252 """Download binary image data.
bkopilovcb1c6292016-06-30 09:20:34 +0300253
OTSUKA, Yuanyingfaac5712016-09-15 13:53:55 +0900254 For a full list of available parameters, please refer to the official
255 API reference:
Andreas Jaegerbf30ae72019-07-22 19:22:57 +0200256 https://docs.openstack.org/api-ref/image/v2/#download-binary-image-data
bkopilovcb1c6292016-06-30 09:20:34 +0300257 """
Andrea Frittolicf40a862016-06-07 14:45:32 +0900258 url = 'images/%s/file' % image_id
Matthew Treinisha62347f2013-03-01 16:37:30 -0500259 resp, body = self.get(url)
zhuflfd5a14b2018-03-16 15:24:17 +0800260 self.expected_success([200, 204, 206], resp.status)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -0800261 return rest_client.ResponseBodyData(resp, body)
Anju Tiwaric4952982013-10-20 07:10:02 +0530262
263 def add_image_tag(self, image_id, tag):
bkopilovcb1c6292016-06-30 09:20:34 +0300264 """Add an image tag.
265
OTSUKA, Yuanyingfaac5712016-09-15 13:53:55 +0900266 For a full list of available parameters, please refer to the official
267 API reference:
Andreas Jaegerbf30ae72019-07-22 19:22:57 +0200268 https://docs.openstack.org/api-ref/image/v2/#add-image-tag
bkopilovcb1c6292016-06-30 09:20:34 +0300269 """
Andrea Frittolicf40a862016-06-07 14:45:32 +0900270 url = 'images/%s/tags/%s' % (image_id, tag)
Valeriy Ponomaryov88686d82014-02-16 12:24:51 +0200271 resp, body = self.put(url, body=None)
David Kranz9c3b3b62014-06-19 16:05:53 -0400272 self.expected_success(204, resp.status)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -0800273 return rest_client.ResponseBody(resp, body)
Anju Tiwaric4952982013-10-20 07:10:02 +0530274
275 def delete_image_tag(self, image_id, tag):
bkopilovcb1c6292016-06-30 09:20:34 +0300276 """Delete an image tag.
277
OTSUKA, Yuanyingfaac5712016-09-15 13:53:55 +0900278 For a full list of available parameters, please refer to the official
279 API reference:
Andreas Jaegerbf30ae72019-07-22 19:22:57 +0200280 https://docs.openstack.org/api-ref/image/v2/#delete-image-tag
bkopilovcb1c6292016-06-30 09:20:34 +0300281 """
Andrea Frittolicf40a862016-06-07 14:45:32 +0900282 url = 'images/%s/tags/%s' % (image_id, tag)
Anju Tiwaric4952982013-10-20 07:10:02 +0530283 resp, _ = self.delete(url)
David Kranz9c3b3b62014-06-19 16:05:53 -0400284 self.expected_success(204, resp.status)
Ken'ichi Ohmichie76510d2016-03-02 10:33:48 -0800285 return rest_client.ResponseBody(resp)