6fe2f74ea4715555510df7ca257eb9f4ed463509
[escalator.git] / client / escalatorclient / v1 / versions.py
1 # Copyright 2012 OpenStack Foundation
2 # 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
16 import copy
17
18 from oslo.utils import encodeutils
19 from oslo.utils import strutils
20 import six
21 import six.moves.urllib.parse as urlparse
22
23 from escalatorclient.common import utils
24 from escalatorclient.openstack.common.apiclient import base
25 from escalatorclient.common.http import HTTPClient
26
27 CREATE_PARAMS = ('id', 'name', 'description', 'type', 'version', 'size',
28                  'checksum', 'status', 'os_status', 'version_patch')
29
30 DEFAULT_PAGE_SIZE = 200
31 VERSION_PARAMS = ('type')
32 SORT_DIR_VALUES = ('asc', 'desc')
33 SORT_KEY_VALUES = (
34     'name', 'id', 'cluster_id', 'created_at', 'updated_at', 'status')
35
36 OS_REQ_ID_HDR = 'x-openstack-request-id'
37
38
39 class Version(base.Resource):
40
41     def __repr__(self):
42         return "<Version %s>" % self._info
43
44     def update(self, **fields):
45         self.manager.update(self, **fields)
46
47     def delete(self, **kwargs):
48         return self.manager.delete(self)
49
50     def data(self, **kwargs):
51         return self.manager.data(self, **kwargs)
52
53
54 class VersionManager(base.ManagerWithFind):
55     resource_class = Version
56
57     def get_version_client(self):
58         endpoint = "http://127.0.0.1:19292"
59         client = HTTPClient(endpoint)
60         return client
61
62     def _list(self, url, response_key, obj_class=None, body=None):
63         version_client = self.get_version_client()
64         resp, body = version_client.get(url)
65
66         if obj_class is None:
67             obj_class = self.resource_class
68
69         data = body[response_key]
70         return ([obj_class(self, res, loaded=True) for res in data if res],
71                 resp)
72
73     def _version_meta_from_headers(self, headers):
74         meta = {'properties': {}}
75         safe_decode = encodeutils.safe_decode
76         for key, value in six.iteritems(headers):
77             value = safe_decode(value, incoming='utf-8')
78             if key.startswith('x-image-meta-property-'):
79                 _key = safe_decode(key[22:], incoming='utf-8')
80                 meta['properties'][_key] = value
81             elif key.startswith('x-image-meta-'):
82                 _key = safe_decode(key[13:], incoming='utf-8')
83                 meta[_key] = value
84
85         for key in ['is_public', 'protected', 'deleted']:
86             if key in meta:
87                 meta[key] = strutils.bool_from_string(meta[key])
88
89         return self._format_version_meta_for_user(meta)
90
91     def _version_meta_to_headers(self, fields):
92         headers = {}
93         fields_copy = copy.deepcopy(fields)
94         for key, value in six.iteritems(fields_copy):
95             headers['%s' % key] = utils.to_str(value)
96         return headers
97
98     @staticmethod
99     def _format_version_meta_for_user(meta):
100         for key in ['size', 'min_ram', 'min_disk']:
101             if key in meta:
102                 try:
103                     meta[key] = int(meta[key]) if meta[key] else 0
104                 except ValueError:
105                     pass
106         return meta
107
108     def _build_params(self, parameters):
109         params = {'limit': parameters.get('page_size', DEFAULT_PAGE_SIZE)}
110
111         if 'marker' in parameters:
112             params['marker'] = parameters['marker']
113
114         sort_key = parameters.get('sort_key')
115         if sort_key is not None:
116             if sort_key in SORT_KEY_VALUES:
117                 params['sort_key'] = sort_key
118             else:
119                 raise ValueError('sort_key must be one of the following: %s.'
120                                  % ', '.join(SORT_KEY_VALUES))
121
122         sort_dir = parameters.get('sort_dir')
123         if sort_dir is not None:
124             if sort_dir in SORT_DIR_VALUES:
125                 params['sort_dir'] = sort_dir
126             else:
127                 raise ValueError('sort_dir must be one of the following: %s.'
128                                  % ', '.join(SORT_DIR_VALUES))
129
130         filters = parameters.get('filters', {})
131         params.update(filters)
132
133         return params
134
135     def list(self, **kwargs):
136         """Get a list of versions.
137
138         :param page_size: number of items to request in each paginated request
139         :param limit: maximum number of versions to return
140         :param marker:begin returning versions that appear later in version
141                        list than that represented by this version id
142         :param filters: dict of direct comparison filters that mimics the
143                         structure of an version object
144         :param return_request_id: If an empty list is provided, populate this
145                               list with the request ID value from the header
146                               x-openstack-request-id
147         :rtype: list of :class:`version`
148         """
149         absolute_limit = kwargs.get('limit')
150         page_size = kwargs.get('page_size', DEFAULT_PAGE_SIZE)
151
152         def paginate(qp, return_request_id=None):
153             for param, value in six.iteritems(qp):
154                 if isinstance(value, six.string_types):
155                     # Note(flaper87) Url encoding should
156                     # be moved inside http utils, at least
157                     # shouldn't be here.
158                     #
159                     # Making sure all params are str before
160                     # trying to encode them
161                     qp[param] = encodeutils.safe_decode(value)
162
163             url = '/v1/versions?%s' % urlparse.urlencode(qp)
164             versions, resp = self._list(url, "versions")
165
166             if return_request_id is not None:
167                 return_request_id.append(resp.headers.get(OS_REQ_ID_HDR, None))
168
169             for version in versions:
170                 yield version
171
172         return_request_id = kwargs.get('return_req_id', None)
173
174         params = self._build_params(kwargs)
175
176         seen = 0
177         while True:
178             seen_last_page = 0
179             filtered = 0
180             for version in paginate(params, return_request_id):
181                 last_version = version.id
182
183                 if (absolute_limit is not None and
184                         seen + seen_last_page >= absolute_limit):
185                     # Note(kragniz): we've seen enough images
186                     return
187                 else:
188                     seen_last_page += 1
189                     yield version
190
191             seen += seen_last_page
192
193             if seen_last_page + filtered == 0:
194                 # Note(kragniz): we didn't get any versions in the last page
195                 return
196
197             if absolute_limit is not None and seen >= absolute_limit:
198                 # Note(kragniz): reached the limit of versions to return
199                 return
200
201             if page_size and seen_last_page + filtered < page_size:
202                 # Note(kragniz): we've reached the last page of the versions
203                 return
204
205             # Note(kragniz): there are more versions to come
206             params['marker'] = last_version
207             seen_last_page = 0
208
209     def version(self, **kwargs):
210         """Get internal or external version of escalator.
211
212         TODO(bcwaldon): document accepted params
213         """
214         fields = {}
215         for field in kwargs:
216             if field in VERSION_PARAMS:
217                 fields[field] = kwargs[field]
218             else:
219                 msg = 'install() got an unexpected keyword argument \'%s\''
220                 raise TypeError(msg % field)
221
222         url = '/v1/versions'
223         hdrs = self._version_meta_to_headers(fields)
224         resp, body = self.client.post(url, headers=None, data=hdrs)
225         return Version(self, body)