1 # Copyright 2012 OpenStack Foundation
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
8 # http://www.apache.org/licenses/LICENSE-2.0
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
18 from oslo_utils import encodeutils
19 from oslo_utils import strutils
21 import six.moves.urllib.parse as urlparse
23 from escalatorclient.common import utils
24 from escalatorclient.openstack.common.apiclient import base
26 CREATE_PARAMS = ('id', 'name', 'description', 'type', 'version', 'size',
27 'checksum', 'status', 'os_status', 'version_patch')
29 DEFAULT_PAGE_SIZE = 200
30 VERSION_PARAMS = ('type')
31 SORT_DIR_VALUES = ('asc', 'desc')
33 'name', 'id', 'cluster_id', 'created_at', 'updated_at', 'status')
35 OS_REQ_ID_HDR = 'x-openstack-request-id'
38 class Version(base.Resource):
41 return "<Version %s>" % self._info
43 def update(self, **fields):
44 self.manager.update(self, **fields)
46 def delete(self, **kwargs):
47 return self.manager.delete(self)
49 def data(self, **kwargs):
50 return self.manager.data(self, **kwargs)
53 class VersionManager(base.ManagerWithFind):
54 resource_class = Version
56 def _list(self, url, response_key, obj_class=None, body=None):
57 resp, body = self.client.get(url)
60 obj_class = self.resource_class
62 data = body[response_key]
63 return ([obj_class(self, res, loaded=True) for res in data if res],
66 def _version_meta_from_headers(self, headers):
67 meta = {'properties': {}}
68 safe_decode = encodeutils.safe_decode
69 for key, value in six.iteritems(headers):
70 value = safe_decode(value, incoming='utf-8')
71 if key.startswith('x-image-meta-property-'):
72 _key = safe_decode(key[22:], incoming='utf-8')
73 meta['properties'][_key] = value
74 elif key.startswith('x-image-meta-'):
75 _key = safe_decode(key[13:], incoming='utf-8')
78 for key in ['is_public', 'protected', 'deleted']:
80 meta[key] = strutils.bool_from_string(meta[key])
82 return self._format_version_meta_for_user(meta)
84 def _version_meta_to_headers(self, fields):
86 fields_copy = copy.deepcopy(fields)
87 for key, value in six.iteritems(fields_copy):
88 headers['%s' % key] = utils.to_str(value)
92 def _format_version_meta_for_user(meta):
93 for key in ['size', 'min_ram', 'min_disk']:
96 meta[key] = int(meta[key]) if meta[key] else 0
101 def get(self, version, **kwargs):
102 """Get the metadata for a specific version.
104 :param version: image object or id to look up
105 :rtype: :class:`version`
107 version_id = base.getid(version)
108 resp, body = self.client.get('/v1/versions/%s'
109 % urlparse.quote(str(version_id)))
110 # meta = self._version_meta_from_headers(resp.headers)
111 return_request_id = kwargs.get('return_req_id', None)
112 if return_request_id is not None:
113 return_request_id.append(resp.headers.get(OS_REQ_ID_HDR, None))
114 # return version(self, meta)
115 return Version(self, self._format_version_meta_for_user(
118 def _build_params(self, parameters):
119 params = {'limit': parameters.get('page_size', DEFAULT_PAGE_SIZE)}
121 if 'marker' in parameters:
122 params['marker'] = parameters['marker']
124 sort_key = parameters.get('sort_key')
125 if sort_key is not None:
126 if sort_key in SORT_KEY_VALUES:
127 params['sort_key'] = sort_key
129 raise ValueError('sort_key must be one of the following: %s.'
130 % ', '.join(SORT_KEY_VALUES))
132 sort_dir = parameters.get('sort_dir')
133 if sort_dir is not None:
134 if sort_dir in SORT_DIR_VALUES:
135 params['sort_dir'] = sort_dir
137 raise ValueError('sort_dir must be one of the following: %s.'
138 % ', '.join(SORT_DIR_VALUES))
140 filters = parameters.get('filters', {})
141 params.update(filters)
145 def list(self, **kwargs):
146 """Get a list of versions.
148 :param page_size: number of items to request in each paginated request
149 :param limit: maximum number of versions to return
150 :param marker:begin returning versions that appear later in version
151 list than that represented by this version id
152 :param filters: dict of direct comparison filters that mimics the
153 structure of an version object
154 :param return_request_id: If an empty list is provided, populate this
155 list with the request ID value from the header
156 x-openstack-request-id
157 :rtype: list of :class:`version`
159 absolute_limit = kwargs.get('limit')
160 page_size = kwargs.get('page_size', DEFAULT_PAGE_SIZE)
162 def paginate(qp, return_request_id=None):
163 for param, value in six.iteritems(qp):
164 if isinstance(value, six.string_types):
165 # Note(flaper87) Url encoding should
166 # be moved inside http utils, at least
169 # Making sure all params are str before
170 # trying to encode them
171 qp[param] = encodeutils.safe_decode(value)
173 url = '/v1/versions?%s' % urlparse.urlencode(qp)
174 versions, resp = self._list(url, "versions")
176 if return_request_id is not None:
177 return_request_id.append(resp.headers.get(OS_REQ_ID_HDR, None))
179 for version in versions:
182 return_request_id = kwargs.get('return_req_id', None)
184 params = self._build_params(kwargs)
190 for version in paginate(params, return_request_id):
191 last_version = version.id
193 if (absolute_limit is not None and
194 seen + seen_last_page >= absolute_limit):
195 # Note(kragniz): we've seen enough images
201 seen += seen_last_page
203 if seen_last_page + filtered == 0:
204 # Note(kragniz): we didn't get any versions in the last page
207 if absolute_limit is not None and seen >= absolute_limit:
208 # Note(kragniz): reached the limit of versions to return
211 if page_size and seen_last_page + filtered < page_size:
212 # Note(kragniz): we've reached the last page of the versions
215 # Note(kragniz): there are more versions to come
216 params['marker'] = last_version
219 def add(self, **kwargs):
222 TODO(bcwaldon): document accepted params
227 if field in CREATE_PARAMS:
228 fields[field] = kwargs[field]
229 elif field == 'return_req_id':
232 msg = 'create() got an unexpected keyword argument \'%s\''
233 raise TypeError(msg % field)
235 hdrs = self._version_meta_to_headers(fields)
237 resp, body = self.client.post('/v1/versions',
240 return_request_id = kwargs.get('return_req_id', None)
241 if return_request_id is not None:
242 return_request_id.append(resp.headers.get(OS_REQ_ID_HDR, None))
244 return Version(self, self._format_version_meta_for_user(
247 def delete(self, version, **kwargs):
248 """Delete an version."""
249 url = "/v1/versions/%s" % base.getid(version)
250 resp, body = self.client.delete(url)
251 return_request_id = kwargs.get('return_req_id', None)
252 if return_request_id is not None:
253 return_request_id.append(resp.headers.get(OS_REQ_ID_HDR, None))
255 def update(self, version, **kwargs):
258 TODO(bcwaldon): document accepted params
263 if field in CREATE_PARAMS:
264 fields[field] = kwargs[field]
265 elif field == 'return_req_id':
267 hdrs.update(self._version_meta_to_headers(fields))
269 url = '/v1/versions/%s' % base.getid(version)
270 resp, body = self.client.put(url, headers=None, data=hdrs)
271 return_request_id = kwargs.get('return_req_id', None)
272 if return_request_id is not None:
273 return_request_id.append(resp.headers.get(OS_REQ_ID_HDR, None))
275 return Version(self, self._format_version_meta_for_user(
276 body['version_meta']))
278 def version(self, **kwargs):
279 """Get internal or external version of escalator.
281 TODO(bcwaldon): document accepted params
285 if field in VERSION_PARAMS:
286 fields[field] = kwargs[field]
288 msg = 'install() got an unexpected keyword argument \'%s\''
289 raise TypeError(msg % field)
292 hdrs = self._restore_meta_to_headers(fields)
293 resp, body = self.client.post(url, headers=None, data=hdrs)
294 return Version(self, body)