blob: 277075ea6250d0c4554a0e36d6c1a0b90bd52db2 [file] [log] [blame]
Matthew Treinish72ea4422013-02-07 14:42:49 -05001# vim: tabstop=4 shiftwidth=4 softtabstop=4
2#
3# Copyright 2013 IBM
4# All Rights Reserved.
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18import copy
Attila Fazekas5c068812013-02-14 15:29:44 +010019import errno
Matthew Treinish72ea4422013-02-07 14:42:49 -050020import json
21import os
22import time
23import urllib
24
25from tempest.common import glance_http
26from tempest.common.rest_client import RestClient
27from tempest import exceptions
Matthew Treinish72ea4422013-02-07 14:42:49 -050028
29
30class ImageClientJSON(RestClient):
31
32 def __init__(self, config, username, password, auth_url, tenant_name=None):
33 super(ImageClientJSON, self).__init__(config, username, password,
34 auth_url, tenant_name)
35 self.service = self.config.images.catalog_type
36 self.http = self._get_http()
37
38 def _image_meta_from_headers(self, headers):
39 meta = {'properties': {}}
40 for key, value in headers.iteritems():
41 if key.startswith('x-image-meta-property-'):
42 _key = key[22:]
43 meta['properties'][_key] = value
44 elif key.startswith('x-image-meta-'):
45 _key = key[13:]
46 meta[_key] = value
47
48 for key in ['is_public', 'protected', 'deleted']:
49 if key in meta:
50 meta[key] = meta[key].strip().lower() in ('t', 'true', 'yes',
51 '1')
52 for key in ['size', 'min_ram', 'min_disk']:
53 if key in meta:
54 try:
55 meta[key] = int(meta[key])
56 except ValueError:
57 pass
58 return meta
59
60 def _image_meta_to_headers(self, fields):
61 headers = {}
62 fields_copy = copy.deepcopy(fields)
63 for key, value in fields_copy.pop('properties', {}).iteritems():
64 headers['x-image-meta-property-%s' % key] = str(value)
65 for key, value in fields_copy.iteritems():
66 headers['x-image-meta-%s' % key] = str(value)
67 return headers
68
69 def _get_file_size(self, obj):
70 """Analyze file-like object and attempt to determine its size.
71
72 :param obj: file-like object, typically redirected from stdin.
73 :retval The file's size or None if it cannot be determined.
74 """
75 # For large images, we need to supply the size of the
76 # image file. See LP Bugs #827660 and #845788.
77 if hasattr(obj, 'seek') and hasattr(obj, 'tell'):
78 try:
79 obj.seek(0, os.SEEK_END)
80 obj_size = obj.tell()
81 obj.seek(0)
82 return obj_size
83 except IOError, e:
84 if e.errno == errno.ESPIPE:
85 # Illegal seek. This means the user is trying
86 # to pipe image data to the client, e.g.
87 # echo testdata | bin/glance add blah..., or
88 # that stdin is empty, or that a file-like
89 # object which doesn't support 'seek/tell' has
90 # been supplied.
91 return None
92 else:
93 raise
94 else:
95 # Cannot determine size of input image
96 return None
97
98 def _get_http(self):
Matthew Treinish801f3aa2013-02-28 15:35:03 -050099 token, endpoint = self.keystone_auth(self.user,
100 self.password,
101 self.auth_url,
102 self.service,
103 self.tenant_name)
Matthew Treinish72ea4422013-02-07 14:42:49 -0500104 dscv = self.config.identity.disable_ssl_certificate_validation
105 return glance_http.HTTPClient(endpoint=endpoint, token=token,
106 insecure=dscv)
107
108 def _create_with_data(self, headers, data):
109 resp, body_iter = self.http.raw_request('POST', '/v1/images',
110 headers=headers, body=data)
111 self._error_checker('POST', '/v1/images', headers, data, resp,
112 body_iter)
113 body = json.loads(''.join([c for c in body_iter]))
114 return resp, body['image']
115
116 def _update_with_data(self, image_id, headers, data):
117 url = '/v1/images/%s' % image_id
118 resp, body_iter = self.http.raw_request('PUT', url, headers=headers,
119 body=data)
120 self._error_checker('PUT', url, headers, data,
121 resp, body_iter)
122 body = json.loads(''.join([c for c in body_iter]))
123 return resp, body['image']
124
125 def create_image(self, name, container_format, disk_format, is_public=True,
126 location=None, properties=None, data=None):
127 params = {
128 "name": name,
129 "container_format": container_format,
130 "disk_format": disk_format,
131 "is_public": is_public,
132 }
133 headers = {}
134
135 if location is not None:
136 params['location'] = location
137
138 if properties is not None:
139 params['properties'] = properties
140
141 headers.update(self._image_meta_to_headers(params))
142
143 if data is not None:
144 return self._create_with_data(headers, data)
145
146 resp, body = self.post('v1/images', data, headers)
147 body = json.loads(body)
148 return resp, body['image']
149
150 def update_image(self, image_id, name=None, container_format=None,
151 data=None):
152 params = {}
153 headers = {}
154 if name is not None:
155 params['name'] = name
156
157 if container_format is not None:
158 params['container_format'] = container_format
159
160 headers.update(self._image_meta_to_headers(params))
161
162 if data is not None:
163 return self._update_with_data(image_id, headers, data)
164
165 url = 'v1/images/%s' % image_id
166 resp, body = self.put(url, data, headers)
167 body = json.loads(body)
168 return resp, body['image']
169
170 def delete_image(self, image_id):
171 url = 'v1/images/%s' % image_id
Matthew Treinish801f3aa2013-02-28 15:35:03 -0500172 self.delete(url)
Matthew Treinish72ea4422013-02-07 14:42:49 -0500173
Attila Fazekas11795b52013-02-24 15:49:08 +0100174 def image_list(self, **kwargs):
Matthew Treinish72ea4422013-02-07 14:42:49 -0500175 url = 'v1/images'
176
Attila Fazekas11795b52013-02-24 15:49:08 +0100177 if len(kwargs) > 0:
178 url += '?%s' % urllib.urlencode(kwargs)
179
180 resp, body = self.get(url)
181 body = json.loads(body)
182 return resp, body['images']
183
184 def image_list_detail(self, **kwargs):
185 url = 'v1/images/detail'
186
187 if len(kwargs) > 0:
188 url += '?%s' % urllib.urlencode(kwargs)
Matthew Treinish72ea4422013-02-07 14:42:49 -0500189
190 resp, body = self.get(url)
191 body = json.loads(body)
192 return resp, body['images']
193
194 def get_image(self, image_id, wait=None):
195 url = 'v1/images/%s' % image_id
196 resp, __ = self.get(url, wait=wait)
197 body = self._image_meta_from_headers(resp)
198 return resp, body
199
200 def is_resource_deleted(self, id):
201 try:
202 self.get_image(id, wait=True)
203 except exceptions.NotFound:
204 return True
205 return False